Skip to content

Retries #1141

@lovelydinosaur

Description

@lovelydinosaur

Our yardstick for 1.0 has generally been to achieve feature parity with requests, plus the following additional support...

  • Sync + Async support.
  • HTTP/2 support.
  • Strict timeout defaults.
  • Type annotations throughtout.
  • Streaming multipart uploads.

Something we've so far omitted is retires=..., which are not included in either the requests QuickStart guide, or in the Advanced Usage section. However requests does have some retry support, can be enabled by mounting a custom adapter... https://requests.readthedocs.io/en/master/api/#requests.adapters.HTTPAdapter

Screenshot 2020-08-07 at 15 34 27

I'd suggest that if we do want to add retry support we should start off by doing so in a limited fashion, and only provide a simply retries=<int> argument on the client, which matches the same "only retry on connection failures" behaviour that requests defaults to for integer retry arguments.

That doesn't necessarily preclude that we could consider more complex retry behaviour at some point in the future, but I'm prefer we push back on that for as long as possible.

Having more dials for configuration is something we should generally avoid where possible. Also, we've gone to a great deal of effort over nicely spec'ing our Transport API, and it's perfectly feasible for developers to build against that to deal with any more complex behaviours that they'd like to see.

With all that in mind, I'd suggest something we might consider would be...

class Client(..., retries: int=0):
    ...

With the implementation handled inside our _send_single_request method. Something like...

transport = self._transport_for_url(request.url)
retries = self.retries

while True:
    with map_exceptions(HTTPCORE_EXC_MAP, request=request):
        try:
            (
                http_version,
                status_code,
                reason_phrase,
                headers,
                stream,
            ) = transport.request(
                request.method.encode(),
                request.url.raw,
                headers=request.headers.raw,
                stream=request.stream,
                timeout=timeout.as_dict(),
            )
       except (httpcore.ConnectError, httpcore.ConnectTimeout):
            if retries <= 0:
               raise
            retries -= 1

response = Response(
    status_code,
    http_version=http_version.decode("ascii"),
    headers=headers,
    stream=stream,  # type: ignore
    request=request,
)

self.cookies.extract_cookies(response)

status = f"{response.status_code} {response.reason_phrase}"
response_line = f"{response.http_version} {status}"
logger.debug(f'HTTP Request: {request.method} {request.url} "{response_line}"')

return response

I'm still not 100% sure that we want this, but perhaps this is a decent low-impact feature.
Any thoughts?

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestrequests-compatIssues related to Requests backwards compatibility

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions