Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

release: 1.1.1 #113

Merged
merged 2 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "1.1.0"
".": "1.1.1"
}
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## 1.1.1 (2024-09-27)

Full Changelog: [v1.1.0...v1.1.1](https://github.com/OneBusAway/python-sdk/compare/v1.1.0...v1.1.1)

### Chores

* **internal:** codegen related update ([#112](https://github.com/OneBusAway/python-sdk/issues/112)) ([646ad67](https://github.com/OneBusAway/python-sdk/commit/646ad67c30929ccb7bc40e6667a93712284c3c30))

## 1.1.0 (2024-09-11)

Full Changelog: [v1.0.0...v1.1.0](https://github.com/OneBusAway/python-sdk/compare/v1.0.0...v1.1.0)
Expand Down
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ $ pip install -r requirements-dev.lock

## Modifying/Adding code

Most of the SDK is generated code, and any modified code will be overridden on the next generation. The
`src/onebusaway/lib/` and `examples/` directories are exceptions and will never be overridden.
Most of the SDK is generated code. Modifications to code will be persisted between generations, but may
result in merge conflicts between manual patches and changes from the generator. The generator will never
modify the contents of the `src/onebusaway/lib/` and `examples/` directories.

## Adding and running examples

All files in the `examples/` directory are not modified by the Stainless generator and can be freely edited or
added to.
All files in the `examples/` directory are not modified by the generator and can be freely edited or added to.

```bash
# add an example to examples/<your-example>.py
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "onebusaway"
version = "1.1.0"
version = "1.1.1"
description = "The official Python library for the onebusaway-sdk API"
dynamic = ["readme"]
license = "Apache-2.0"
Expand Down
6 changes: 3 additions & 3 deletions requirements-dev.lock
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ markdown-it-py==3.0.0
# via rich
mdurl==0.1.2
# via markdown-it-py
mypy==1.10.1
mypy==1.11.2
mypy-extensions==1.0.0
# via mypy
nodeenv==1.8.0
Expand All @@ -71,7 +71,7 @@ pydantic-core==2.18.2
# via pydantic
pygments==2.18.0
# via rich
pyright==1.1.374
pyright==1.1.380
pytest==7.1.1
# via pytest-asyncio
pytest-asyncio==0.21.1
Expand All @@ -81,7 +81,7 @@ pytz==2023.3.post1
# via dirty-equals
respx==0.20.2
rich==13.7.1
ruff==0.5.6
ruff==0.6.5
setuptools==68.2.2
# via nodeenv
six==1.16.0
Expand Down
111 changes: 63 additions & 48 deletions src/onebusaway/_base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,14 +400,7 @@ def _make_status_error(
) -> _exceptions.APIStatusError:
raise NotImplementedError()

def _remaining_retries(
self,
remaining_retries: Optional[int],
options: FinalRequestOptions,
) -> int:
return remaining_retries if remaining_retries is not None else options.get_max_retries(self.max_retries)

def _build_headers(self, options: FinalRequestOptions) -> httpx.Headers:
def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0) -> httpx.Headers:
custom_headers = options.headers or {}
headers_dict = _merge_mappings(self.default_headers, custom_headers)
self._validate_headers(headers_dict, custom_headers)
Expand All @@ -419,6 +412,11 @@ def _build_headers(self, options: FinalRequestOptions) -> httpx.Headers:
if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers:
headers[idempotency_header] = options.idempotency_key or self._idempotency_key()

# Don't set the retry count header if it was already set or removed by the caller. We check
# `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case.
if "x-stainless-retry-count" not in (header.lower() for header in custom_headers):
headers["x-stainless-retry-count"] = str(retries_taken)

return headers

def _prepare_url(self, url: str) -> URL:
Expand All @@ -440,6 +438,8 @@ def _make_sse_decoder(self) -> SSEDecoder | SSEBytesDecoder:
def _build_request(
self,
options: FinalRequestOptions,
*,
retries_taken: int = 0,
) -> httpx.Request:
if log.isEnabledFor(logging.DEBUG):
log.debug("Request options: %s", model_dump(options, exclude_unset=True))
Expand All @@ -455,7 +455,7 @@ def _build_request(
else:
raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`")

headers = self._build_headers(options)
headers = self._build_headers(options, retries_taken=retries_taken)
params = _merge_mappings(self.default_query, options.params)
content_type = headers.get("Content-Type")
files = options.files
Expand Down Expand Up @@ -489,12 +489,17 @@ def _build_request(
if not files:
files = cast(HttpxRequestFiles, ForceMultipartDict())

prepared_url = self._prepare_url(options.url)
if "_" in prepared_url.host:
# work around https://github.com/encode/httpx/discussions/2880
kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")}

# TODO: report this error to httpx
return self._client.build_request( # pyright: ignore[reportUnknownMemberType]
headers=headers,
timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout,
method=options.method,
url=self._prepare_url(options.url),
url=prepared_url,
# the `Query` type that we use is incompatible with qs'
# `Params` type as it needs to be typed as `Mapping[str, object]`
# so that passing a `TypedDict` doesn't cause an error.
Expand Down Expand Up @@ -933,20 +938,25 @@ def request(
stream: bool = False,
stream_cls: type[_StreamT] | None = None,
) -> ResponseT | _StreamT:
if remaining_retries is not None:
retries_taken = options.get_max_retries(self.max_retries) - remaining_retries
else:
retries_taken = 0

return self._request(
cast_to=cast_to,
options=options,
stream=stream,
stream_cls=stream_cls,
remaining_retries=remaining_retries,
retries_taken=retries_taken,
)

def _request(
self,
*,
cast_to: Type[ResponseT],
options: FinalRequestOptions,
remaining_retries: int | None,
retries_taken: int,
stream: bool,
stream_cls: type[_StreamT] | None,
) -> ResponseT | _StreamT:
Expand All @@ -958,8 +968,8 @@ def _request(
cast_to = self._maybe_override_cast_to(cast_to, options)
options = self._prepare_options(options)

retries = self._remaining_retries(remaining_retries, options)
request = self._build_request(options)
remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
request = self._build_request(options, retries_taken=retries_taken)
self._prepare_request(request)

kwargs: HttpxSendArgs = {}
Expand All @@ -977,11 +987,11 @@ def _request(
except httpx.TimeoutException as err:
log.debug("Encountered httpx.TimeoutException", exc_info=True)

if retries > 0:
if remaining_retries > 0:
return self._retry_request(
input_options,
cast_to,
retries,
retries_taken=retries_taken,
stream=stream,
stream_cls=stream_cls,
response_headers=None,
Expand All @@ -992,11 +1002,11 @@ def _request(
except Exception as err:
log.debug("Encountered Exception", exc_info=True)

if retries > 0:
if remaining_retries > 0:
return self._retry_request(
input_options,
cast_to,
retries,
retries_taken=retries_taken,
stream=stream,
stream_cls=stream_cls,
response_headers=None,
Expand All @@ -1019,13 +1029,13 @@ def _request(
except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
log.debug("Encountered httpx.HTTPStatusError", exc_info=True)

if retries > 0 and self._should_retry(err.response):
if remaining_retries > 0 and self._should_retry(err.response):
err.response.close()
return self._retry_request(
input_options,
cast_to,
retries,
err.response.headers,
retries_taken=retries_taken,
response_headers=err.response.headers,
stream=stream,
stream_cls=stream_cls,
)
Expand All @@ -1044,26 +1054,26 @@ def _request(
response=response,
stream=stream,
stream_cls=stream_cls,
retries_taken=options.get_max_retries(self.max_retries) - retries,
retries_taken=retries_taken,
)

def _retry_request(
self,
options: FinalRequestOptions,
cast_to: Type[ResponseT],
remaining_retries: int,
response_headers: httpx.Headers | None,
*,
retries_taken: int,
response_headers: httpx.Headers | None,
stream: bool,
stream_cls: type[_StreamT] | None,
) -> ResponseT | _StreamT:
remaining = remaining_retries - 1
if remaining == 1:
remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
if remaining_retries == 1:
log.debug("1 retry left")
else:
log.debug("%i retries left", remaining)
log.debug("%i retries left", remaining_retries)

timeout = self._calculate_retry_timeout(remaining, options, response_headers)
timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers)
log.info("Retrying request to %s in %f seconds", options.url, timeout)

# In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a
Expand All @@ -1073,7 +1083,7 @@ def _retry_request(
return self._request(
options=options,
cast_to=cast_to,
remaining_retries=remaining,
retries_taken=retries_taken + 1,
stream=stream,
stream_cls=stream_cls,
)
Expand Down Expand Up @@ -1491,12 +1501,17 @@ async def request(
stream_cls: type[_AsyncStreamT] | None = None,
remaining_retries: Optional[int] = None,
) -> ResponseT | _AsyncStreamT:
if remaining_retries is not None:
retries_taken = options.get_max_retries(self.max_retries) - remaining_retries
else:
retries_taken = 0

return await self._request(
cast_to=cast_to,
options=options,
stream=stream,
stream_cls=stream_cls,
remaining_retries=remaining_retries,
retries_taken=retries_taken,
)

async def _request(
Expand All @@ -1506,7 +1521,7 @@ async def _request(
*,
stream: bool,
stream_cls: type[_AsyncStreamT] | None,
remaining_retries: int | None,
retries_taken: int,
) -> ResponseT | _AsyncStreamT:
if self._platform is None:
# `get_platform` can make blocking IO calls so we
Expand All @@ -1521,8 +1536,8 @@ async def _request(
cast_to = self._maybe_override_cast_to(cast_to, options)
options = await self._prepare_options(options)

retries = self._remaining_retries(remaining_retries, options)
request = self._build_request(options)
remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
request = self._build_request(options, retries_taken=retries_taken)
await self._prepare_request(request)

kwargs: HttpxSendArgs = {}
Expand All @@ -1538,11 +1553,11 @@ async def _request(
except httpx.TimeoutException as err:
log.debug("Encountered httpx.TimeoutException", exc_info=True)

if retries > 0:
if remaining_retries > 0:
return await self._retry_request(
input_options,
cast_to,
retries,
retries_taken=retries_taken,
stream=stream,
stream_cls=stream_cls,
response_headers=None,
Expand All @@ -1553,11 +1568,11 @@ async def _request(
except Exception as err:
log.debug("Encountered Exception", exc_info=True)

if retries > 0:
if retries_taken > 0:
return await self._retry_request(
input_options,
cast_to,
retries,
retries_taken=retries_taken,
stream=stream,
stream_cls=stream_cls,
response_headers=None,
Expand All @@ -1575,13 +1590,13 @@ async def _request(
except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
log.debug("Encountered httpx.HTTPStatusError", exc_info=True)

if retries > 0 and self._should_retry(err.response):
if remaining_retries > 0 and self._should_retry(err.response):
await err.response.aclose()
return await self._retry_request(
input_options,
cast_to,
retries,
err.response.headers,
retries_taken=retries_taken,
response_headers=err.response.headers,
stream=stream,
stream_cls=stream_cls,
)
Expand All @@ -1600,34 +1615,34 @@ async def _request(
response=response,
stream=stream,
stream_cls=stream_cls,
retries_taken=options.get_max_retries(self.max_retries) - retries,
retries_taken=retries_taken,
)

async def _retry_request(
self,
options: FinalRequestOptions,
cast_to: Type[ResponseT],
remaining_retries: int,
response_headers: httpx.Headers | None,
*,
retries_taken: int,
response_headers: httpx.Headers | None,
stream: bool,
stream_cls: type[_AsyncStreamT] | None,
) -> ResponseT | _AsyncStreamT:
remaining = remaining_retries - 1
if remaining == 1:
remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
if remaining_retries == 1:
log.debug("1 retry left")
else:
log.debug("%i retries left", remaining)
log.debug("%i retries left", remaining_retries)

timeout = self._calculate_retry_timeout(remaining, options, response_headers)
timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers)
log.info("Retrying request to %s in %f seconds", options.url, timeout)

await anyio.sleep(timeout)

return await self._request(
options=options,
cast_to=cast_to,
remaining_retries=remaining,
retries_taken=retries_taken + 1,
stream=stream,
stream_cls=stream_cls,
)
Expand Down
Loading