Skip to content
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 @@
{
".": "2.3.0"
".": "2.4.0"
}
6 changes: 3 additions & 3 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 6
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/channel3%2Fpublic-sdk-a86114ce098255360b65356eedfe9c93f9db44aa99cb90d8c36756d39c2c2de0.yml
openapi_spec_hash: 113158785b160e8b67d66e2820137df8
configured_endpoints: 5
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/channel3%2Fpublic-sdk-3366fbfe5ea0c833c184c33d00d301e23e23c0cfa7398b0ebc34a90ab03f65fd.yml
openapi_spec_hash: e428021f51d697d779a5ddd3ee7109b7
config_hash: 0ec132fef7cbcef12aebece85f2ef2b1
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

## 2.4.0 (2025-11-10)

Full Changelog: [v2.3.0...v2.4.0](https://github.com/channel3-ai/sdk-python/compare/v2.3.0...v2.4.0)

### Features

* **api:** api update ([f50613e](https://github.com/channel3-ai/sdk-python/commit/f50613e9cf5d2067b282896dfafcb73ef99ee0ae))


### Bug Fixes

* **client:** close streams without requiring full consumption ([4d44bb1](https://github.com/channel3-ai/sdk-python/commit/4d44bb1d045d7d2447bc305dd8463e27dd170175))


### Chores

* **internal/tests:** avoid race condition with implicit client cleanup ([ef7fe91](https://github.com/channel3-ai/sdk-python/commit/ef7fe91e5e5c14e15172642362aee07b96f19b3d))
* **internal:** grammar fix (it's -> its) ([ae7ab14](https://github.com/channel3-ai/sdk-python/commit/ae7ab1494bc21b8b5aeefb91af3353b696949bcb))

## 2.3.0 (2025-10-28)

Full Changelog: [v2.2.1...v2.3.0](https://github.com/channel3-ai/sdk-python/compare/v2.2.1...v2.3.0)
Expand Down
5 changes: 2 additions & 3 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,12 @@ Methods:
Types:

```python
from channel3_sdk.types import Brand, BrandListResponse
from channel3_sdk.types import Brand
```

Methods:

- <code title="get /v0/brands/{brand_id}">client.brands.<a href="./src/channel3_sdk/resources/brands.py">retrieve</a>(brand_id) -> <a href="./src/channel3_sdk/types/brand.py">Brand</a></code>
- <code title="get /v0/brands">client.brands.<a href="./src/channel3_sdk/resources/brands.py">list</a>(\*\*<a href="src/channel3_sdk/types/brand_list_params.py">params</a>) -> <a href="./src/channel3_sdk/types/brand_list_response.py">BrandListResponse</a></code>
- <code title="get /v0/brands">client.brands.<a href="./src/channel3_sdk/resources/brands.py">list</a>(\*\*<a href="src/channel3_sdk/types/brand_list_params.py">params</a>) -> <a href="./src/channel3_sdk/types/brand.py">Brand</a></code>

# Enrich

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 = "channel3_sdk"
version = "2.3.0"
version = "2.4.0"
description = "The official Python library for the channel3 API"
dynamic = ["readme"]
license = "Apache-2.0"
Expand Down
10 changes: 4 additions & 6 deletions src/channel3_sdk/_streaming.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,8 @@ def __stream__(self) -> Iterator[_T]:
for sse in iterator:
yield process_data(data=sse.json(), cast_to=cast_to, response=response)

# Ensure the entire stream is consumed
for _sse in iterator:
...
# As we might not fully consume the response stream, we need to close it explicitly
response.close()

def __enter__(self) -> Self:
return self
Expand Down Expand Up @@ -121,9 +120,8 @@ async def __stream__(self) -> AsyncIterator[_T]:
async for sse in iterator:
yield process_data(data=sse.json(), cast_to=cast_to, response=response)

# Ensure the entire stream is consumed
async for _sse in iterator:
...
# As we might not fully consume the response stream, we need to close it explicitly
await response.aclose()

async def __aenter__(self) -> Self:
return self
Expand Down
2 changes: 1 addition & 1 deletion src/channel3_sdk/_utils/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]:
# Type safe methods for narrowing types with TypeVars.
# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown],
# however this cause Pyright to rightfully report errors. As we know we don't
# care about the contained types we can safely use `object` in it's place.
# care about the contained types we can safely use `object` in its place.
#
# There are two separate functions defined, `is_*` and `is_*_t` for different use cases.
# `is_*` is for when you're dealing with an unknown input
Expand Down
2 changes: 1 addition & 1 deletion src/channel3_sdk/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

__title__ = "channel3_sdk"
__version__ = "2.3.0" # x-release-please-version
__version__ = "2.4.0" # x-release-please-version
121 changes: 11 additions & 110 deletions src/channel3_sdk/resources/brands.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

from __future__ import annotations

from typing import Optional

import httpx

from ..types import brand_list_params
from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
from .._types import Body, Query, Headers, NotGiven, not_given
from .._utils import maybe_transform, async_maybe_transform
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
Expand All @@ -19,7 +17,6 @@
)
from ..types.brand import Brand
from .._base_client import make_request_options
from ..types.brand_list_response import BrandListResponse

__all__ = ["BrandsResource", "AsyncBrandsResource"]

Expand All @@ -44,54 +41,19 @@ def with_streaming_response(self) -> BrandsResourceWithStreamingResponse:
"""
return BrandsResourceWithStreamingResponse(self)

def retrieve(
self,
brand_id: str,
*,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Brand:
"""
Get detailed information for a specific brand by its ID.

Args:
extra_headers: Send extra headers

extra_query: Add additional query parameters to the request

extra_body: Add additional JSON properties to the request

timeout: Override the client-level default timeout for this request, in seconds
"""
if not brand_id:
raise ValueError(f"Expected a non-empty value for `brand_id` but received {brand_id!r}")
return self._get(
f"/v0/brands/{brand_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=Brand,
)

def list(
self,
*,
page: int | Omit = omit,
query: Optional[str] | Omit = omit,
size: int | Omit = omit,
query: str,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> BrandListResponse:
) -> Brand:
"""
Get all brands that the vendor currently sells.
Find a brand by name.

Args:
extra_headers: Send extra headers
Expand All @@ -109,16 +71,9 @@ def list(
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
query=maybe_transform(
{
"page": page,
"query": query,
"size": size,
},
brand_list_params.BrandListParams,
),
query=maybe_transform({"query": query}, brand_list_params.BrandListParams),
),
cast_to=BrandListResponse,
cast_to=Brand,
)


Expand All @@ -142,54 +97,19 @@ def with_streaming_response(self) -> AsyncBrandsResourceWithStreamingResponse:
"""
return AsyncBrandsResourceWithStreamingResponse(self)

async def retrieve(
self,
brand_id: str,
*,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> Brand:
"""
Get detailed information for a specific brand by its ID.

Args:
extra_headers: Send extra headers

extra_query: Add additional query parameters to the request

extra_body: Add additional JSON properties to the request

timeout: Override the client-level default timeout for this request, in seconds
"""
if not brand_id:
raise ValueError(f"Expected a non-empty value for `brand_id` but received {brand_id!r}")
return await self._get(
f"/v0/brands/{brand_id}",
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
cast_to=Brand,
)

async def list(
self,
*,
page: int | Omit = omit,
query: Optional[str] | Omit = omit,
size: int | Omit = omit,
query: str,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> BrandListResponse:
) -> Brand:
"""
Get all brands that the vendor currently sells.
Find a brand by name.

Args:
extra_headers: Send extra headers
Expand All @@ -207,26 +127,16 @@ async def list(
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
query=await async_maybe_transform(
{
"page": page,
"query": query,
"size": size,
},
brand_list_params.BrandListParams,
),
query=await async_maybe_transform({"query": query}, brand_list_params.BrandListParams),
),
cast_to=BrandListResponse,
cast_to=Brand,
)


class BrandsResourceWithRawResponse:
def __init__(self, brands: BrandsResource) -> None:
self._brands = brands

self.retrieve = to_raw_response_wrapper(
brands.retrieve,
)
self.list = to_raw_response_wrapper(
brands.list,
)
Expand All @@ -236,9 +146,6 @@ class AsyncBrandsResourceWithRawResponse:
def __init__(self, brands: AsyncBrandsResource) -> None:
self._brands = brands

self.retrieve = async_to_raw_response_wrapper(
brands.retrieve,
)
self.list = async_to_raw_response_wrapper(
brands.list,
)
Expand All @@ -248,9 +155,6 @@ class BrandsResourceWithStreamingResponse:
def __init__(self, brands: BrandsResource) -> None:
self._brands = brands

self.retrieve = to_streamed_response_wrapper(
brands.retrieve,
)
self.list = to_streamed_response_wrapper(
brands.list,
)
Expand All @@ -260,9 +164,6 @@ class AsyncBrandsResourceWithStreamingResponse:
def __init__(self, brands: AsyncBrandsResource) -> None:
self._brands = brands

self.retrieve = async_to_streamed_response_wrapper(
brands.retrieve,
)
self.list = async_to_streamed_response_wrapper(
brands.list,
)
6 changes: 4 additions & 2 deletions src/channel3_sdk/resources/enrich.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ def enrich_url(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> EnrichEnrichURLResponse:
"""
Enrich a product URL with additional information.
Search by product URL, get back full product information from Channel3’s product
database.

Args:
url: The URL of the product to enrich
Expand Down Expand Up @@ -108,7 +109,8 @@ async def enrich_url(
timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> EnrichEnrichURLResponse:
"""
Enrich a product URL with additional information.
Search by product URL, get back full product information from Channel3’s product
database.

Args:
url: The URL of the product to enrich
Expand Down
6 changes: 4 additions & 2 deletions src/channel3_sdk/resources/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ def perform(

context: Optional customer information to personalize search results

filters: Optional filters
filters: Optional filters. Search will only consider products that match all of the
filters.

image_url: Image URL

Expand Down Expand Up @@ -154,7 +155,8 @@ async def perform(

context: Optional customer information to personalize search results

filters: Optional filters
filters: Optional filters. Search will only consider products that match all of the
filters.

image_url: Image URL

Expand Down
1 change: 0 additions & 1 deletion src/channel3_sdk/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from .variant import Variant as Variant
from .brand_list_params import BrandListParams as BrandListParams
from .availability_status import AvailabilityStatus as AvailabilityStatus
from .brand_list_response import BrandListResponse as BrandListResponse
from .search_perform_params import SearchPerformParams as SearchPerformParams
from .search_perform_response import SearchPerformResponse as SearchPerformResponse
from .enrich_enrich_url_params import EnrichEnrichURLParams as EnrichEnrichURLParams
Expand Down
3 changes: 3 additions & 0 deletions src/channel3_sdk/types/brand.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ class Brand(BaseModel):

name: str

best_commission_rate: Optional[float] = None
"""The maximum commission rate for the brand, as a percentage"""

description: Optional[str] = None

logo_url: Optional[str] = None
9 changes: 2 additions & 7 deletions src/channel3_sdk/types/brand_list_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,10 @@

from __future__ import annotations

from typing import Optional
from typing_extensions import TypedDict
from typing_extensions import Required, TypedDict

__all__ = ["BrandListParams"]


class BrandListParams(TypedDict, total=False):
page: int

query: Optional[str]

size: int
query: Required[str]
Loading