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 @@
{
".": "0.17.0"
".": "0.18.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: 65
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-e914e2d08b888c77051acb09176d5e88052f130e0d22e85d946a675d2c3d39ab.yml
openapi_spec_hash: 611d0ed1b4519331470b5d14e5f6784a
config_hash: 3ded7a0ed77b1bfd68eabc6763521fe8
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/kernel%2Fkernel-015c11efc34c81d4d82a937c017f5eb789ea3ca21a05b70e2ed31c069b839293.yml
openapi_spec_hash: 3dcab2044da305f376cecf4eca38caee
config_hash: 0fbdda3a736cc2748ca33371871e61b3
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## 0.18.0 (2025-10-30)

Full Changelog: [v0.17.0...v0.18.0](https://github.com/onkernel/kernel-python-sdk/compare/v0.17.0...v0.18.0)

### Features

* apps: add offset pagination + headers ([54ea6e7](https://github.com/onkernel/kernel-python-sdk/commit/54ea6e79ba21ba5ab33ab14a1bf257a95bf76bb8))


### Bug Fixes

* **client:** close streams without requiring full consumption ([4014c3d](https://github.com/onkernel/kernel-python-sdk/commit/4014c3d53c36627ecd702b8af272a382529aef55))

## 0.17.0 (2025-10-27)

Full Changelog: [v0.16.0...v0.17.0](https://github.com/onkernel/kernel-python-sdk/compare/v0.16.0...v0.17.0)
Expand Down
2 changes: 1 addition & 1 deletion api.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ from kernel.types import AppListResponse

Methods:

- <code title="get /apps">client.apps.<a href="./src/kernel/resources/apps.py">list</a>(\*\*<a href="src/kernel/types/app_list_params.py">params</a>) -> <a href="./src/kernel/types/app_list_response.py">AppListResponse</a></code>
- <code title="get /apps">client.apps.<a href="./src/kernel/resources/apps.py">list</a>(\*\*<a href="src/kernel/types/app_list_params.py">params</a>) -> <a href="./src/kernel/types/app_list_response.py">SyncOffsetPagination[AppListResponse]</a></code>

# Invocations

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 = "kernel"
version = "0.17.0"
version = "0.18.0"
description = "The official Python library for the kernel API"
dynamic = ["readme"]
license = "Apache-2.0"
Expand Down
10 changes: 4 additions & 6 deletions src/kernel/_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/kernel/_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__ = "kernel"
__version__ = "0.17.0" # x-release-please-version
__version__ = "0.18.0" # x-release-please-version
39 changes: 29 additions & 10 deletions src/kernel/resources/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

from ..types import app_list_params
from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
from .._utils import maybe_transform, async_maybe_transform
from .._utils import maybe_transform
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
from .._response import (
Expand All @@ -15,7 +15,8 @@
async_to_raw_response_wrapper,
async_to_streamed_response_wrapper,
)
from .._base_client import make_request_options
from ..pagination import SyncOffsetPagination, AsyncOffsetPagination
from .._base_client import AsyncPaginator, make_request_options
from ..types.app_list_response import AppListResponse

__all__ = ["AppsResource", "AsyncAppsResource"]
Expand Down Expand Up @@ -45,21 +46,27 @@ def list(
self,
*,
app_name: str | Omit = omit,
limit: int | Omit = omit,
offset: int | Omit = omit,
version: str | Omit = omit,
# 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,
) -> AppListResponse:
) -> SyncOffsetPagination[AppListResponse]:
"""List applications.

Optionally filter by app name and/or version label.

Args:
app_name: Filter results by application name.

limit: Limit the number of app to return.

offset: Offset the number of app to return.

version: Filter results by version label.

extra_headers: Send extra headers
Expand All @@ -70,8 +77,9 @@ def list(

timeout: Override the client-level default timeout for this request, in seconds
"""
return self._get(
return self._get_api_list(
"/apps",
page=SyncOffsetPagination[AppListResponse],
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
Expand All @@ -80,12 +88,14 @@ def list(
query=maybe_transform(
{
"app_name": app_name,
"limit": limit,
"offset": offset,
"version": version,
},
app_list_params.AppListParams,
),
),
cast_to=AppListResponse,
model=AppListResponse,
)


Expand All @@ -109,25 +119,31 @@ def with_streaming_response(self) -> AsyncAppsResourceWithStreamingResponse:
"""
return AsyncAppsResourceWithStreamingResponse(self)

async def list(
def list(
self,
*,
app_name: str | Omit = omit,
limit: int | Omit = omit,
offset: int | Omit = omit,
version: str | Omit = omit,
# 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,
) -> AppListResponse:
) -> AsyncPaginator[AppListResponse, AsyncOffsetPagination[AppListResponse]]:
"""List applications.

Optionally filter by app name and/or version label.

Args:
app_name: Filter results by application name.

limit: Limit the number of app to return.

offset: Offset the number of app to return.

version: Filter results by version label.

extra_headers: Send extra headers
Expand All @@ -138,22 +154,25 @@ async def list(

timeout: Override the client-level default timeout for this request, in seconds
"""
return await self._get(
return self._get_api_list(
"/apps",
page=AsyncOffsetPagination[AppListResponse],
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
query=await async_maybe_transform(
query=maybe_transform(
{
"app_name": app_name,
"limit": limit,
"offset": offset,
"version": version,
},
app_list_params.AppListParams,
),
),
cast_to=AppListResponse,
model=AppListResponse,
)


Expand Down
6 changes: 6 additions & 0 deletions src/kernel/types/app_list_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,11 @@ class AppListParams(TypedDict, total=False):
app_name: str
"""Filter results by application name."""

limit: int
"""Limit the number of app to return."""

offset: int
"""Offset the number of app to return."""

version: str
"""Filter results by version label."""
9 changes: 3 additions & 6 deletions src/kernel/types/app_list_response.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

from typing import Dict, List
from typing_extensions import Literal, TypeAlias
from typing_extensions import Literal

from .._models import BaseModel
from .shared.app_action import AppAction

__all__ = ["AppListResponse", "AppListResponseItem"]
__all__ = ["AppListResponse"]


class AppListResponseItem(BaseModel):
class AppListResponse(BaseModel):
id: str
"""Unique identifier for the app version"""

Expand All @@ -30,6 +30,3 @@ class AppListResponseItem(BaseModel):

version: str
"""Version label for the application"""


AppListResponse: TypeAlias = List[AppListResponseItem]
21 changes: 13 additions & 8 deletions tests/api_resources/test_apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from kernel import Kernel, AsyncKernel
from tests.utils import assert_matches_type
from kernel.types import AppListResponse
from kernel.pagination import SyncOffsetPagination, AsyncOffsetPagination

base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")

Expand All @@ -21,16 +22,18 @@ class TestApps:
@parametrize
def test_method_list(self, client: Kernel) -> None:
app = client.apps.list()
assert_matches_type(AppListResponse, app, path=["response"])
assert_matches_type(SyncOffsetPagination[AppListResponse], app, path=["response"])

@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
def test_method_list_with_all_params(self, client: Kernel) -> None:
app = client.apps.list(
app_name="app_name",
limit=1,
offset=0,
version="version",
)
assert_matches_type(AppListResponse, app, path=["response"])
assert_matches_type(SyncOffsetPagination[AppListResponse], app, path=["response"])

@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
Expand All @@ -40,7 +43,7 @@ def test_raw_response_list(self, client: Kernel) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
app = response.parse()
assert_matches_type(AppListResponse, app, path=["response"])
assert_matches_type(SyncOffsetPagination[AppListResponse], app, path=["response"])

@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
Expand All @@ -50,7 +53,7 @@ def test_streaming_response_list(self, client: Kernel) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"

app = response.parse()
assert_matches_type(AppListResponse, app, path=["response"])
assert_matches_type(SyncOffsetPagination[AppListResponse], app, path=["response"])

assert cast(Any, response.is_closed) is True

Expand All @@ -64,16 +67,18 @@ class TestAsyncApps:
@parametrize
async def test_method_list(self, async_client: AsyncKernel) -> None:
app = await async_client.apps.list()
assert_matches_type(AppListResponse, app, path=["response"])
assert_matches_type(AsyncOffsetPagination[AppListResponse], app, path=["response"])

@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
async def test_method_list_with_all_params(self, async_client: AsyncKernel) -> None:
app = await async_client.apps.list(
app_name="app_name",
limit=1,
offset=0,
version="version",
)
assert_matches_type(AppListResponse, app, path=["response"])
assert_matches_type(AsyncOffsetPagination[AppListResponse], app, path=["response"])

@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
Expand All @@ -83,7 +88,7 @@ async def test_raw_response_list(self, async_client: AsyncKernel) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
app = await response.parse()
assert_matches_type(AppListResponse, app, path=["response"])
assert_matches_type(AsyncOffsetPagination[AppListResponse], app, path=["response"])

@pytest.mark.skip(reason="Prism tests are disabled")
@parametrize
Expand All @@ -93,6 +98,6 @@ async def test_streaming_response_list(self, async_client: AsyncKernel) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"

app = await response.parse()
assert_matches_type(AppListResponse, app, path=["response"])
assert_matches_type(AsyncOffsetPagination[AppListResponse], app, path=["response"])

assert cast(Any, response.is_closed) is True