Skip to content

fix: normalize transport header extraction and status handling#11

Open
kvdhanush06 wants to merge 1 commit into
RightNow-AI:mainfrom
kvdhanush06:fix/python-transport-header-normalization
Open

fix: normalize transport header extraction and status handling#11
kvdhanush06 wants to merge 1 commit into
RightNow-AI:mainfrom
kvdhanush06:fix/python-transport-header-normalization

Conversation

@kvdhanush06

Copy link
Copy Markdown

Summary

Fixes robustness issues in the Python SDK default transport when handling different HTTP response implementations.

This change:

  • adds fallback status resolution using .getcode() and .code
  • normalizes response headers into canonical lower-case keys
  • removes reliance on response context-manager support

The fix improves compatibility with urllib-style responses, mocked transports, and mixed-case HTTP headers while preserving existing SDK behavior.

Existing SDK response interfaces and downstream behavior remain backward compatible.


Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation only
  • CI / tooling / repo hygiene
  • Security fix

Changes made

Transport status handling

Updated _default_transport() to resolve HTTP status codes using:

  1. response.status
  2. response.getcode()
  3. response.code

This improves compatibility with:

  • urllib
  • mocked/test response objects
  • alternative HTTP response implementations

Header normalization

Normalized all transport response headers into a lower-case dictionary:

headers = {k.lower(): v for k, v in raw_headers}

This removes duplicated casing checks such as:

  • X-Request-Id
  • x-request-id
  • Retry-After
  • retry-after

and ensures consistent downstream header access.


Response lifecycle handling

Removed direct reliance on:

with response:

The transport now:

  • reads response bodies directly
  • safely closes responses if .close() exists
  • works with non-context-managed response implementations

The same compatibility and normalization logic was applied to urllib.error.HTTPError handling paths.


Verification

Validated using:

  • reproduction with a fake response object exposing:
    • getcode()
    • getheaders()
    • no .status
    • no context manager support
  • full Python test suite

Local verification commands:

python -m pytest

All tests passed locally.


Checklist

  • My code follows the style of this project (TS strict, no any in public surface; Python type hints + docstrings)
  • I have added tests for any new behavior
  • All tests pass locally (pnpm test for TS, python -m pytest for Python)
  • I have updated the relevant CHANGELOG.md files (typescript/ and/or python/)
  • I have NOT bumped the SDK version (maintainers handle that at release time)
  • I have NOT added any runtime dependencies (both SDKs are zero-dep by policy)
  • I have NOT exposed any private RunInfra endpoint or internal hostname

Related issues

Closes #10

Copilot AI review requested due to automatic review settings May 25, 2026 07:42
@kvdhanush06 kvdhanush06 requested a review from jaberjaber23 as a code owner May 25, 2026 07:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Improves the Python SDK’s default urllib transport robustness by supporting more HTTP response shapes and normalizing headers for consistent downstream access.

Changes:

  • Add fallback HTTP status resolution via response.status, response.getcode(), and response.code.
  • Normalize response headers into a lower-cased dictionary using .headers / .getheaders() extraction.
  • Remove reliance on response context-manager support and close responses when possible.
Comments suppressed due to low confidence (1)

python/runinfra/init.py:465

  • The urllib.error.HTTPError handling path reads the body but never closes the underlying file/response object. HTTPError typically supports .close(), so skipping it can leak sockets/file descriptors on repeated errors. Mirror the success-path cleanup by calling exc.close() (if available) in a finally after exc.read() (including the read-error branch).
            response_headers = _extract_headers(exc)
            try:
                body = exc.read()
            except Exception as read_exc:
                raise _TransportBodyReadError(

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 419 to 423
while True:
chunk = response.readline()
chunk = getattr(response, "readline", lambda: b"")()
if not chunk:
break
yield chunk
Comment on lines +355 to +359
def _extract_status(obj: Any) -> int:
# Accept common shapes: .status, .getcode(), or .code
if hasattr(obj, "status"):
try:
return int(getattr(obj, "status"))
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.

[bug]: Normalize HTTP response headers and support response.getcode() fallback in Python default transport

2 participants