Skip to content

Commit 21d7ec4

Browse files
authored
Merge pull request #56 from isaacus-dev/release-please--branches--main--changes--next
release: 0.3.3
2 parents 964456f + 38765a3 commit 21d7ec4

File tree

10 files changed

+108
-10
lines changed

10 files changed

+108
-10
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ jobs:
1212
lint:
1313
name: lint
1414
runs-on: ubuntu-latest
15-
1615
steps:
1716
- uses: actions/checkout@v4
1817

@@ -33,7 +32,6 @@ jobs:
3332
test:
3433
name: test
3534
runs-on: ubuntu-latest
36-
3735
steps:
3836
- uses: actions/checkout@v4
3937

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "0.3.2"
2+
".": "0.3.3"
33
}

.stats.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
configured_endpoints: 2
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/isaacus%2Fisaacus-861e8a85f0fb73cf4b7fc6c2b27722072ff33109459e90c17be24af15dfcbd0c.yml
3-
openapi_spec_hash: 644a0383600633ee604bb1e5b9ca025d
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/isaacus%2Fisaacus-d58ccd91625a3b12fd8d1ceece128b604010bd840096000287c927cb5dcf79eb.yml
3+
openapi_spec_hash: 22c8c973d55f26649e9df96c89ea537f
44
config_hash: 1d603d50b7183a492ad6df5f728a1863

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
# Changelog
22

3+
## 0.3.3 (2025-04-16)
4+
5+
Full Changelog: [v0.3.2...v0.3.3](https://github.com/isaacus-dev/isaacus-python/compare/v0.3.2...v0.3.3)
6+
7+
### Bug Fixes
8+
9+
* **perf:** optimize some hot paths ([eee757b](https://github.com/isaacus-dev/isaacus-python/commit/eee757ba44a895fcf2052b9981783b6cf233653f))
10+
* **perf:** skip traversing types for NotGiven values ([7705a99](https://github.com/isaacus-dev/isaacus-python/commit/7705a99e0efd9724eb3260550b4b58081af85878))
11+
12+
13+
### Chores
14+
15+
* **client:** minor internal fixes ([a8dad58](https://github.com/isaacus-dev/isaacus-python/commit/a8dad5881d0f3f5d1929574efba483a8fcdbc322))
16+
* **internal:** codegen related update ([93cdfa0](https://github.com/isaacus-dev/isaacus-python/commit/93cdfa0c0dfc947ec76f10291887b90324301b32))
17+
* **internal:** expand CI branch coverage ([cc5df77](https://github.com/isaacus-dev/isaacus-python/commit/cc5df7771a9ea699b0e37533070e1cb5569d7ad9))
18+
* **internal:** reduce CI branch coverage ([2cb8fb8](https://github.com/isaacus-dev/isaacus-python/commit/2cb8fb81f4cea76d12ae3feeb09e4b43b743e8c4))
19+
* **internal:** slight transform perf improvement ([6f47eaf](https://github.com/isaacus-dev/isaacus-python/commit/6f47eafa0ebcd31741f24bea539a4c54e88a758e))
20+
* **internal:** update pyright settings ([7dd9ad4](https://github.com/isaacus-dev/isaacus-python/commit/7dd9ad4a4a25825929a4916168a07d74bcc52fbe))
21+
22+
23+
### Documentation
24+
25+
* **api:** removed description of certain objects due to Mintlify bug ([9099926](https://github.com/isaacus-dev/isaacus-python/commit/90999261a360fef3ba92c52e4ad5361b79b499e6))
26+
327
## 0.3.2 (2025-04-04)
428

529
Full Changelog: [v0.3.1...v0.3.2](https://github.com/isaacus-dev/isaacus-python/compare/v0.3.1...v0.3.2)

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "isaacus"
3-
version = "0.3.2"
3+
version = "0.3.3"
44
description = "The official Python library for the isaacus API"
55
dynamic = ["readme"]
66
license = "Apache-2.0"
@@ -147,6 +147,7 @@ exclude = [
147147
]
148148

149149
reportImplicitOverride = true
150+
reportOverlappingOverload = false
150151

151152
reportImportCycles = false
152153
reportPrivateUsage = false

src/isaacus/_base_client.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,8 @@ def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0
409409

410410
idempotency_header = self._idempotency_header
411411
if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers:
412-
headers[idempotency_header] = options.idempotency_key or self._idempotency_key()
412+
options.idempotency_key = options.idempotency_key or self._idempotency_key()
413+
headers[idempotency_header] = options.idempotency_key
413414

414415
# Don't set these headers if they were already set or removed by the caller. We check
415416
# `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case.
@@ -943,6 +944,10 @@ def _request(
943944
request = self._build_request(options, retries_taken=retries_taken)
944945
self._prepare_request(request)
945946

947+
if options.idempotency_key:
948+
# ensure the idempotency key is reused between requests
949+
input_options.idempotency_key = options.idempotency_key
950+
946951
kwargs: HttpxSendArgs = {}
947952
if self.custom_auth is not None:
948953
kwargs["auth"] = self.custom_auth
@@ -1475,6 +1480,10 @@ async def _request(
14751480
request = self._build_request(options, retries_taken=retries_taken)
14761481
await self._prepare_request(request)
14771482

1483+
if options.idempotency_key:
1484+
# ensure the idempotency key is reused between requests
1485+
input_options.idempotency_key = options.idempotency_key
1486+
14781487
kwargs: HttpxSendArgs = {}
14791488
if self.custom_auth is not None:
14801489
kwargs["auth"] = self.custom_auth

src/isaacus/_utils/_transform.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
import pathlib
66
from typing import Any, Mapping, TypeVar, cast
77
from datetime import date, datetime
8-
from typing_extensions import Literal, get_args, override, get_type_hints
8+
from typing_extensions import Literal, get_args, override, get_type_hints as _get_type_hints
99

1010
import anyio
1111
import pydantic
1212

1313
from ._utils import (
1414
is_list,
15+
is_given,
16+
lru_cache,
1517
is_mapping,
1618
is_iterable,
1719
)
@@ -108,6 +110,7 @@ class Params(TypedDict, total=False):
108110
return cast(_T, transformed)
109111

110112

113+
@lru_cache(maxsize=8096)
111114
def _get_annotated_type(type_: type) -> type | None:
112115
"""If the given type is an `Annotated` type then it is returned, if not `None` is returned.
113116
@@ -142,6 +145,10 @@ def _maybe_transform_key(key: str, type_: type) -> str:
142145
return key
143146

144147

148+
def _no_transform_needed(annotation: type) -> bool:
149+
return annotation == float or annotation == int
150+
151+
145152
def _transform_recursive(
146153
data: object,
147154
*,
@@ -184,6 +191,15 @@ def _transform_recursive(
184191
return cast(object, data)
185192

186193
inner_type = extract_type_arg(stripped_type, 0)
194+
if _no_transform_needed(inner_type):
195+
# for some types there is no need to transform anything, so we can get a small
196+
# perf boost from skipping that work.
197+
#
198+
# but we still need to convert to a list to ensure the data is json-serializable
199+
if is_list(data):
200+
return data
201+
return list(data)
202+
187203
return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data]
188204

189205
if is_union_type(stripped_type):
@@ -245,6 +261,11 @@ def _transform_typeddict(
245261
result: dict[str, object] = {}
246262
annotations = get_type_hints(expected_type, include_extras=True)
247263
for key, value in data.items():
264+
if not is_given(value):
265+
# we don't need to include `NotGiven` values here as they'll
266+
# be stripped out before the request is sent anyway
267+
continue
268+
248269
type_ = annotations.get(key)
249270
if type_ is None:
250271
# we do not have a type annotation for this field, leave it as is
@@ -332,6 +353,15 @@ async def _async_transform_recursive(
332353
return cast(object, data)
333354

334355
inner_type = extract_type_arg(stripped_type, 0)
356+
if _no_transform_needed(inner_type):
357+
# for some types there is no need to transform anything, so we can get a small
358+
# perf boost from skipping that work.
359+
#
360+
# but we still need to convert to a list to ensure the data is json-serializable
361+
if is_list(data):
362+
return data
363+
return list(data)
364+
335365
return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data]
336366

337367
if is_union_type(stripped_type):
@@ -393,10 +423,25 @@ async def _async_transform_typeddict(
393423
result: dict[str, object] = {}
394424
annotations = get_type_hints(expected_type, include_extras=True)
395425
for key, value in data.items():
426+
if not is_given(value):
427+
# we don't need to include `NotGiven` values here as they'll
428+
# be stripped out before the request is sent anyway
429+
continue
430+
396431
type_ = annotations.get(key)
397432
if type_ is None:
398433
# we do not have a type annotation for this field, leave it as is
399434
result[key] = value
400435
else:
401436
result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_)
402437
return result
438+
439+
440+
@lru_cache(maxsize=8096)
441+
def get_type_hints(
442+
obj: Any,
443+
globalns: dict[str, Any] | None = None,
444+
localns: Mapping[str, Any] | None = None,
445+
include_extras: bool = False,
446+
) -> dict[str, Any]:
447+
return _get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras)

src/isaacus/_utils/_typing.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
get_origin,
1414
)
1515

16+
from ._utils import lru_cache
1617
from .._types import InheritsGeneric
1718
from .._compat import is_union as _is_union
1819

@@ -66,6 +67,7 @@ def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]:
6667

6768

6869
# Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]]
70+
@lru_cache(maxsize=8096)
6971
def strip_annotated_type(typ: type) -> type:
7072
if is_required_type(typ) or is_annotated_type(typ):
7173
return strip_annotated_type(cast(type, get_args(typ)[0]))

src/isaacus/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
22

33
__title__ = "isaacus"
4-
__version__ = "0.3.2" # x-release-please-version
4+
__version__ = "0.3.3" # x-release-please-version

tests/test_transform.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import pytest
1010

11-
from isaacus._types import Base64FileInput
11+
from isaacus._types import NOT_GIVEN, Base64FileInput
1212
from isaacus._utils import (
1313
PropertyInfo,
1414
transform as _transform,
@@ -432,3 +432,22 @@ async def test_base64_file_input(use_async: bool) -> None:
432432
assert await transform({"foo": io.BytesIO(b"Hello, world!")}, TypedDictBase64Input, use_async) == {
433433
"foo": "SGVsbG8sIHdvcmxkIQ=="
434434
} # type: ignore[comparison-overlap]
435+
436+
437+
@parametrize
438+
@pytest.mark.asyncio
439+
async def test_transform_skipping(use_async: bool) -> None:
440+
# lists of ints are left as-is
441+
data = [1, 2, 3]
442+
assert await transform(data, List[int], use_async) is data
443+
444+
# iterables of ints are converted to a list
445+
data = iter([1, 2, 3])
446+
assert await transform(data, Iterable[int], use_async) == [1, 2, 3]
447+
448+
449+
@parametrize
450+
@pytest.mark.asyncio
451+
async def test_strips_notgiven(use_async: bool) -> None:
452+
assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"}
453+
assert await transform({"foo_bar": NOT_GIVEN}, Foo1, use_async) == {}

0 commit comments

Comments
 (0)