Skip to content

Conversation

aaguiarz
Copy link
Member

@aaguiarz aaguiarz commented Aug 14, 2025

Summary

  • allow custom headers on individual Python client calls via options["headers"]
  • document headers option on client methods
  • document change in changelog

Testing

  • make test-client-python (fails: docker: not found)

https://chatgpt.com/codex/tasks/task_e_689e4bfaadcc832291107ea947202073

Summary by CodeRabbit

  • New Features
    • Support setting custom HTTP headers per request via an options headers parameter.
  • Documentation
    • Changelog updated to include the new per-request headers capability.
  • Refactor
    • Standardized internal header handling and naming for consistency; no user-facing behavior changes.

@aaguiarz aaguiarz requested a review from a team as a code owner August 14, 2025 23:48
Copy link
Contributor

coderabbitai bot commented Aug 14, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Renames helper function set_heading_if_not_set to set_header_if_not_set across Python client templates, updates all call sites and docstrings, and adds a changelog entry documenting per-request custom headers via options["headers"]. No behavioral changes beyond naming and header dict alignment.

Changes

Cohort / File(s) Summary of Changes
Changelog update
config/clients/python/CHANGELOG.md.mustache
Added Unreleased entry: “feat: allow per-request custom headers via options["headers"]”.
Async client helper rename
config/clients/python/template/src/client/client.py.mustache
Renamed set_heading_if_not_set → set_header_if_not_set; updated call sites and docstrings; clarified options_to_kwargs docs.
Sync client helper rename
config/clients/python/template/src/sync/client/client.py.mustache
Replaced set_heading_if_not_set with set_header_if_not_set; standardized header handling via options["headers"]; updated docstrings.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/update-python-sdk-to-include-headers

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🔭 Outside diff range comments (2)
config/clients/python/template/src/client/client.py.mustache (1)

65-85: Avoid mutating caller options and simplify header detection

Current implementation mutates the caller’s options dict and relies on type checks to decide whether to set a header. This can leak SDK-injected headers across subsequent calls when a user reuses the same options object and makes the logic brittle.

  • Create a shallow copy of options and a fresh headers dict to avoid mutating the input.
  • Check presence by key, not by value type.
  • Always reassign the new headers dict back to the (copied) options.

Apply this diff:

 def set_header_if_not_set(
     options: dict[str, int | str | dict[str, int | str]] | None,
     name: str,
     value: str,
 ) -> dict[str, int | str | dict[str, int | str]]:
     """
-    Set header to the value if it is not set
+    Set header to the value if it is not set
     """
-    _options: dict[str, int | str | dict[str, int | str]] = (
-        options if options is not None else {}
-    )
-
-    if type(_options.get("headers")) is not dict:
-        _options["headers"] = {}
-
-    if type(_options["headers"]) is dict:
-        if type(_options["headers"].get(name)) not in [int, str]:
-            _options["headers"][name] = value
-
-    return _options
+    # copy options to avoid mutating the caller's dict (common when options are reused)
+    _options: dict[str, int | str | dict[str, int | str]] = dict(options) if options else {}
+    # copy headers to avoid mutating caller's nested dict
+    headers = dict(_options.get("headers") or {})
+    if name not in headers:
+        headers[name] = value
+    _options["headers"] = headers
+    return _options
config/clients/python/template/src/sync/client/client.py.mustache (1)

65-85: Avoid mutating caller options and simplify header detection

Mirror the async client change: copy the options dict and headers sub-dict before injecting default headers, and decide by key presence rather than value type.

Apply this diff:

 def set_header_if_not_set(
     options: dict[str, int | str | dict[str, int | str]] | None,
     name: str,
     value: str,
 ) -> dict[str, int | str | dict[str, int | str]]:
     """
-    Set header to the value if it is not set
+    Set header to the value if it is not set
     """
-    _options: dict[str, int | str | dict[str, int | str]] = (
-        options if options is not None else {}
-    )
-
-    if type(_options.get("headers")) is not dict:
-        _options["headers"] = {}
-
-    if type(_options["headers"]) is dict:
-        if type(_options["headers"].get(name)) not in [int, str]:
-            _options["headers"][name] = value
-
-    return _options
+    _options: dict[str, int | str | dict[str, int | str]] = dict(options) if options else {}
+    headers = dict(_options.get("headers") or {})
+    if name not in headers:
+        headers[name] = value
+    _options["headers"] = headers
+    return _options
🧹 Nitpick comments (2)
config/clients/python/CHANGELOG.md.mustache (1)

8-8: Changelog entry: add PR reference for traceability

Recommend referencing the PR number for this feature to aid tracking and release notes consistency.

Apply this diff:

-- feat: allow per-request custom headers via `options["headers"]`
+- feat: allow per-request custom headers via `options["headers"]` (#596)
config/clients/python/template/src/client/client.py.mustache (1)

818-819: Fix typo in docstring ("request11")

There's an extra "11" suffix in the consistency line.

Apply this diff:

-        :param consistency(options) - The type of consistency preferred for the request11
+        :param consistency(options) - The type of consistency preferred for the request
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7c39d23 and 637e7f2.

📒 Files selected for processing (3)
  • config/clients/python/CHANGELOG.md.mustache (1 hunks)
  • config/clients/python/template/src/client/client.py.mustache (24 hunks)
  • config/clients/python/template/src/sync/client/client.py.mustache (24 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build-and-test-java-sdk
  • GitHub Check: build-and-test-go-sdk
🔇 Additional comments (1)
config/clients/python/template/src/client/client.py.mustache (1)

65-69: Rename verified — no lingering set_heading_if_not_set

I searched the repository for the old helper name and found no matches. Only the new helper set_header_if_not_set is present in the template files:

  • config/clients/python/template/src/client/client.py.mustache (definition + multiple usages)
  • config/clients/python/template/src/sync/client/client.py.mustache (definition + multiple usages)

No further action required.

Comment on lines +91 to 92
Return kwargs with continuation_token, page_size, headers and retry parameters
"""
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Docstrings: standardize option names to snake_case

Docstrings use camelCase retryParams, maxRetry, minWaitInMs, while code expects retry_params (and downstream likely max_retry, min_wait_in_ms). For Python, prefer snake_case in docs to match actual option keys and PEP 8.

To locate all occurrences to update across both async and sync templates:

Also applies to: 224-228, 239-243, 254-258, 269-272, 286-290, 301-305, 316-320, 332-336, 353-357, 377-382, 479-483, 505-509, 518-522, 540-544, 601-605, 667-671, 749-754, 780-785, 814-819, 848-853, 878-883, 910-914, 926-930


🏁 Script executed:

#!/bin/bash
rg -n -C2 -g 'config/clients/python/template/src/**/client.py.mustache' -P 'retryParams|minWaitInMs|maxRetry'

Length of output: 46361


🏁 Script executed:

#!/bin/bash
# Find implementation(s) and usages of options_to_kwargs
rg -n --hidden --glob '!**/node_modules/**' -S "options_to_kwargs" || true
# If found, show surrounding context for the implementation (if any file path returned)
FILE=$(rg -n --hidden --glob '!**/node_modules/**' -S "def options_to_kwargs|options_to_kwargs =" -g '**' -l || true)
if [ -n "$FILE" ]; then
  echo "== Showing matches in: $FILE =="
  sed -n '1,240p' "$FILE"
else
  echo "No explicit 'def options_to_kwargs' found; showing usages instead."
  rg -n --hidden --glob '!**/node_modules/**' -S "options_to_kwargs" -C3 || true
fi

Length of output: 5280


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILES=(
  "config/clients/python/template/src/client/client.py.mustache"
  "config/clients/python/template/src/sync/client/client.py.mustache"
)

for f in "${FILES[@]}"; do
  if [ -f "$f" ]; then
    echo "== $f : lines 1-200 =="
    sed -n '1,200p' "$f"
    echo
    echo "== $f : lines 200-400 =="
    sed -n '200,400p' "$f"
    echo
  else
    echo "MISSING: $f"
  fi
done

echo "== grep for retryParams / maxRetry / minWaitInMs in those files =="
rg -n --hidden -S 'retryParams|maxRetry|minWaitInMs|retry_params|max_retry|min_wait_in_ms' "${FILES[@]}" || true

Length of output: 61351


Docstrings: use snake_case for retry options in Python templates

options_to_kwargs expects snake_case keys (e.g. "retry_params"), but docstrings use camelCase (retryParams, maxRetry, minWaitInMs). Update the docstrings so they match the code/PEP8.

Files to update (multiple occurrences in each):

  • config/clients/python/template/src/client/client.py.mustache
  • config/clients/python/template/src/sync/client/client.py.mustache

Suggested replacements (apply to every docstring occurrence of these params):

  • :param retryParams(options) - Override the retry parameters for this request
  • :param retry_params(options) - Override the retry parameters for this request
  • :param retryParams.maxRetry(options) - Override the max number of retries on each API request
  • :param retry_params.max_retry(options) - Override the max number of retries on each API request
  • :param retryParams.minWaitInMs(options) - Override the minimum wait before a retry is initiated
  • :param retry_params.min_wait_in_ms(options) - Override the minimum wait before a retry is initiated

You can find all occurrences with:
rg -n -C2 -g 'config/clients/python/template/src/**/client.py.mustache' -P 'retryParams|minWaitInMs|maxRetry'

Comment on lines +337 to 338
options = set_header_if_not_set(options, CLIENT_METHOD_HEADER, "ReadLatestAuthorizationModel")
options["page_size"] = 1
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Prevent stale X-OpenFGA-Client-Method header when reusing options dicts

Because options can be reused by callers, mutating it in-place can leave a previously injected method header in the user's dict, causing the next call to send the wrong method header. The refactor proposed for set_header_if_not_set (copy options and headers) avoids this cross-call contamination while still respecting user-provided headers.

Run this script to check if any other places mutate options directly (beyond temporary local copies), which could still leak state across calls:

Also applies to: 484-485, 490-491, 510-511, 524-525, 606-608, 672-674, 854-856


🏁 Script executed:

#!/bin/bash
# Look for direct mutations to options (assignments to options[...] = or options.pop)
rg -n -C2 --type=py.mustache -P '(?<!def\s)\soptions\[[' -g 'config/clients/python/template/src/**/client.py.mustache'
rg -n -C2 --type=py.mustache -P '\boptions\.pop\(' -g 'config/clients/python/template/src/**/client.py.mustache'

Length of output: 358


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Searching for client.py.mustache files..."
rg --files -g 'config/clients/python/template/src/**/client.py.mustache' || true

echo
echo "Searching for assignments into options[...]"
rg -n -C2 -g 'config/clients/python/template/src/**/client.py.mustache' -P 'options\[[^\]]+\]\s*=' || true

echo
echo "Searching for options.pop(...)"
rg -n -C2 -g 'config/clients/python/template/src/**/client.py.mustache' '\boptions\.pop\(' || true

echo
echo "Searching for options.update(...)"
rg -n -C2 -g 'config/clients/python/template/src/**/client.py.mustache' '\boptions\.update\(' || true

echo
echo "Searching for del options[...]"
rg -n -C2 -g 'config/clients/python/template/src/**/client.py.mustache' '\bdel\s+options\[' || true

echo
echo "Searching for options.clear(...)"
rg -n -C2 -g 'config/clients/python/template/src/**/client.py.mustache' '\boptions\.clear\(' || true

echo
echo "Searching for options.setdefault(...)"
rg -n -C2 -g 'config/clients/python/template/src/**/client.py.mustache' '\boptions\.setdefault\(' || true

Length of output: 5503


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILES=(
  config/clients/python/template/src/client/client.py.mustache
  config/clients/python/template/src/sync/client/client.py.mustache
)

for f in "${FILES[@]}"; do
  if [[ -f "$f" ]]; then
    echo "==== $f ===="
    echo "---- lines 1-140 ----"
    sed -n '1,140p' "$f"
    echo
    echo "---- lines 320-420 ----"
    sed -n '320,420p' "$f"
    echo
    echo "---- lines 360-400 ----"
    sed -n '360,400p' "$f"
    echo
  else
    echo "File not found: $f"
  fi
done

echo "---- Search for set_header_if_not_set and options_to_kwargs ----"
rg -n 'set_header_if_not_set|options_to_kwargs' || true

echo
echo "---- Show any helper function definitions that mention 'headers' and '_options' ----"
rg -n 'def .*options|headers' || true

Length of output: 133444


Action required: stop mutating callers' options dict / headers in Python client templates

set_header_if_not_set currently mutates the caller's dict (it aliases options and headers). I inspected the template files and confirmed multiple call sites that either call set_header_if_not_set then mutate options (e.g. options["page_size"] = 1) or mutate options in-place via pop — this can leak stale X-OpenFGA-Client-Method headers across calls.

Locations to fix

  • config/clients/python/template/src/client/client.py.mustache
    • def set_header_if_not_set — around line 65
    • read_latest_authorization_model — lines 337–338 (options = set_header_if_not_set(...); options["page_size"] = 1)
    • read — ~lines 371–392 (options.pop("page_size"), options.pop("continuation_token"))
    • Other set_header_if_not_set call sites: ~484–485, 490–491, 510–511, 523–525, 606–608, 672–674, 854–856
  • config/clients/python/template/src/sync/client/client.py.mustache
    • def set_header_if_not_set — around line 65
    • read_latest_authorization_model — lines 328–329 (same pattern)
    • read — ~lines 360–392 (options.pop(...) usages)
    • Other set_header_if_not_set call sites found throughout the file (writes, batch check, list relations, etc.)

Recommended minimal change

  • Make set_header_if_not_set return a shallow copy of the options and headers (do not mutate the incoming dict). Example replacement:

def set_header_if_not_set(options: dict | None, name: str, value: str) -> dict:
_options = dict(options) if options is not None else {}
existing_headers = _options.get("headers")
headers = dict(existing_headers) if isinstance(existing_headers, dict) else {}
if not isinstance(headers.get(name), (int, str)):
headers[name] = value
_options["headers"] = headers
return _options

  • Update call sites that mutate options in-place (options.pop(...) or direct assignments) to operate on a local copy (e.g., _opts = dict(options) or use the dict returned from set_header_if_not_set) before popping/setting keys so the caller's dict is not modified.

Rationale: the template must avoid mutating user-supplied dicts so headers (and other per-request options) cannot leak between calls.

Comment on lines +91 to 92
Return kwargs with continuation_token, page_size, headers and retry parameters
"""
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Docstrings: standardize option names to snake_case

Align the docstrings with actual option keys: use retry_params, max_retry, min_wait_in_ms.

Find all occurrences to update:

Also applies to: 215-219, 230-234, 245-249, 259-263, 277-281, 292-296, 307-311, 323-327, 345-348, 368-373, 465-468, 491-494, 503-507, 521-525, 576-581, 635-639, 723-728, 754-759, 788-793, 822-827, 851-856, 884-888, 900-904


🏁 Script executed:

#!/bin/bash
rg -n -C2 -g 'config/clients/python/template/src/**/client.py.mustache' -P 'retryParams|minWaitInMs|maxRetry'

Length of output: 46361


Docstrings: use snake_case keys — retry_params, max_retry, min_wait_in_ms

Docstrings in the Python client templates reference camelCase option keys (retryParams / maxRetry / minWaitInMs). Update them to snake_case to match the actual option keys.

Files / locations to change (multiple occurrences):

  • config/clients/python/template/src/client/client.py.mustache — examples: lines 225-227, 240-242, 255-257, 269-271, 287-289, 302-304, 317-319, 333-335, 354-356, 378-380, 480-482, 506-508, 519-521, 540-542, 602-604, 668-670, 750-752, 781-783, 815-817, 849-851, 879-881, 911-913, 927-929
  • config/clients/python/template/src/sync/client/client.py.mustache — examples: lines 216-218, 231-233, 246-248, 260-262, 278-280, 293-295, 308-310, 324-326, 345-347, 369-371, 465-467, 491-493, 504-506, 521-523, 577-579, 636-638, 724-726, 755-757, 789-791, 823-825, 852-854, 885-887, 901-903

Replacement (apply across the occurrences above):
Replace:
: param retryParams(options) - Override the retry parameters for this request
: param retryParams.maxRetry(options) - Override the max number of retries on each API request
: param retryParams.minWaitInMs(options) - Override the minimum wait before a retry is initiated

With:
: param retry_params(options) - Override the retry parameters for this request
: param retry_params.max_retry(options) - Override the max number of retries on each API request
: param retry_params.min_wait_in_ms(options) - Override the minimum wait before a retry is initiated

🤖 Prompt for AI Agents
config/clients/python/template/src/sync/client/client.py.mustache around lines
91-92 (and apply to the other occurrences listed in the review): docstrings
reference camelCase retry option keys (retryParams, maxRetry, minWaitInMs);
update them to snake_case across all listed locations by replacing the three
lines that document retryParams with the snake_case variants: ": param
retry_params(options) - Override the retry parameters for this request", ":
param retry_params.max_retry(options) - Override the max number of retries on
each API request", and ": param retry_params.min_wait_in_ms(options) - Override
the minimum wait before a retry is initiated".

"""
options = set_heading_if_not_set(options, CLIENT_METHOD_HEADER, "ReadLatestAuthorizationModel")
options = set_header_if_not_set(options, CLIENT_METHOD_HEADER, "ReadLatestAuthorizationModel")
options["page_size"] = 1
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Prevent stale X-OpenFGA-Client-Method header when reusing options dicts

Same rationale as the async client: copying options/headers inside set_header_if_not_set prevents SDK-injected headers from persisting in user-supplied options across calls.

Check for direct mutations of options that might still cause leakage:

Also applies to: 469-469, 475-476, 496-496, 508-509, 582-584, 640-642, 829-830


🏁 Script executed:

#!/bin/bash
rg -n -C2 --type=py.mustache -P '(?<!def\s)\soptions\[[' -g 'config/clients/python/template/src/**/client.py.mustache'
rg -n -C2 --type=py.mustache -P '\boptions\.pop\(' -g 'config/clients/python/template/src/**/client.py.mustache'

Length of output: 358


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Finding client.py.mustache files..."
rg --files -g 'config/clients/python/template/src/**/client.py.mustache' || true

echo
echo "Searching for occurrences that mutate or access 'options'..."
rg -n -C2 -S -e 'options\[' -g 'config/clients/python/template/src/**/client.py.mustache' || true
rg -n -C2 -S -e 'options\.pop\(' -g 'config/clients/python/template/src/**/client.py.mustache' || true
rg -n -C2 -S -e 'options\.update\(' -g 'config/clients/python/template/src/**/client.py.mustache' || true
rg -n -C2 -S -e '^\s*options\s*=' -g 'config/clients/python/template/src/**/client.py.mustache' || true
rg -n -C2 -S -e 'options\.copy\(' -g 'config/clients/python/template/src/**/client.py.mustache' || true
rg -n -C2 -S -e 'headers' -g 'config/clients/python/template/src/**/client.py.mustache' || true

echo
echo "Printing matches with line numbers for manual inspection..."
for f in $(rg --files -g 'config/clients/python/template/src/**/client.py.mustache'); do
  echo
  echo "----- $f -----"
  rg -n -C3 -S -e 'options\[' -g "$f" || true
  rg -n -C3 -S -e 'options\.pop\(' -g "$f" || true
  rg -n -C3 -S -e 'options\.update\(' -g "$f" || true
  rg -n -C3 -S -e 'options\.copy\(' -g "$f" || true
  rg -n -C3 -S -e 'headers' -g "$f" || true
done

Length of output: 142463


Copy options and headers in set_header_if_not_set — avoid mutating caller's dict

set_header_if_not_set currently mutates the passed options dict (and nested headers), so SDK-injected headers (e.g. X-OpenFGA-Client-Method) can persist in user-supplied options across calls. I inspected both sync and async client templates and found direct mutations after set_header_if_not_set and other places that mutate options.

Locations to fix (examples from templates):

  • config/clients/python/template/src/sync/client/client.py.mustache
    • 328: options = set_header_if_not_set(options, CLIENT_METHOD_HEADER, "ReadLatestAuthorizationModel")
    • 329: options["page_size"] = 1
    • 376–382: options.pop("page_size"), options.pop("continuation_token"), kwargs = options_to_kwargs(options)
    • Other set_header_if_not_set usages: 469, 475–476, 495–497, 508–510, 582–584, 640–642, 828–830
  • config/clients/python/template/src/client/client.py.mustache
    • 337: options = set_header_if_not_set(options, CLIENT_METHOD_HEADER, "ReadLatestAuthorizationModel")
    • 338: options["page_size"] = 1
    • 385–392: options.pop("page_size"), options.pop("continuation_token"), kwargs = options_to_kwargs(options)
    • Other set_header_if_not_set usages: 484–491, 506–512, 604–608, 670–674, 852–856

Recommended minimal fix:

  • Make set_header_if_not_set create shallow copies of the options dict and the headers dict before mutating them. Example:

def set_header_if_not_set(options, name, value):
_options = dict(options) if options is not None else {}
headers = dict(_options.get("headers") or {})
if not isinstance(headers.get(name), (int, str)):
headers[name] = value
_options["headers"] = headers
return _options

  • After this change, existing direct mutations (options["page_size"]=1, options.pop(...)) will act on the local copy and will not leak into the caller's dict.

Please update set_header_if_not_set (both sync and async templates) and run a quick grep for remaining direct mutations to confirm no other call-sites rely on mutating the caller's dict.

🤖 Prompt for AI Agents
config/clients/python/template/src/sync/client/client.py.mustache lines 329:
set_header_if_not_set currently mutates the passed-in options dict and its
headers which causes SDK-injected headers to leak into caller-supplied options;
change set_header_if_not_set to create shallow copies of options and headers
(e.g., copy options into a new dict if not None, copy headers into a new dict or
{}), modify the copies to set the header only if not already set, assign the
copied headers back into the copied options, and return the copied options;
after making this change, the existing inline mutations like
options["page_size"] = 1 and options.pop(...) will operate on the returned local
copy and not mutate the caller's dict — repeat the same change for the async
template variant and run a grep across templates for remaining direct mutations
after set_header_if_not_set to ensure no call-sites rely on mutating the
caller's dict.

:param retryParams.minWaitInMs(options) - Override the minimum wait before a retry is initiated
"""
options = set_heading_if_not_set(options, CLIENT_METHOD_HEADER, "Writes")
options = set_header_if_not_set(options, CLIENT_METHOD_HEADER, "Writes")
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Bug: wrong client method header value ("Writes" vs "Write")

The telemetry header should be consistent with the async client and previous naming ("Write"). "Writes" is likely a typo and will skew metrics.

Apply this diff:

-        options = set_header_if_not_set(options, CLIENT_METHOD_HEADER, "Writes")
+        options = set_header_if_not_set(options, CLIENT_METHOD_HEADER, "Write")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
options = set_header_if_not_set(options, CLIENT_METHOD_HEADER, "Writes")
options = set_header_if_not_set(options, CLIENT_METHOD_HEADER, "Write")
🤖 Prompt for AI Agents
In config/clients/python/template/src/sync/client/client.py.mustache around line
469, the telemetry client method header is set to "Writes" (plural) which is
incorrect; change the header value to "Write" to match the async client and
existing naming. Update the call so options = set_header_if_not_set(options,
CLIENT_METHOD_HEADER, "Write") ensuring consistency across clients and
preventing skewed telemetry.

@evansims
Copy link
Member

evansims commented Oct 4, 2025

Superceded by #631, #630, and #627

@evansims evansims closed this Oct 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants