Skip to content

Commit f40e63d

Browse files
authored
Merge pull request #4 from kernel/release-please--branches--main--changes--next
release: 0.28.0
2 parents dc1383b + 97672ac commit f40e63d

10 files changed

Lines changed: 214 additions & 7 deletions

File tree

.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.27.0"
2+
".": "0.28.0"
33
}

.stats.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
configured_endpoints: 62
1+
configured_endpoints: 63

CHANGELOG.md

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

3+
## [0.28.0](https://github.com/kernel/hypeman-python/compare/v0.27.0...v0.28.0) (2026-09-01)
4+
5+
6+
### Features
7+
8+
* Add local image tagging on shared content storage ([a466b24](https://github.com/kernel/hypeman-python/commit/a466b241a26272ca1e1e1c5ba0d787c3e26ed582))
9+
310
## [0.27.0](https://github.com/kernel/hypeman-python/compare/v0.2.0...v0.27.0) (2026-08-31)
411

512

api.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ Methods:
4141
Types:
4242

4343
```python
44-
from hypeman.types import Image, ImageListResponse
44+
from hypeman.types import Image, TagImageRequest, ImageListResponse
4545
```
4646

4747
Methods:
@@ -50,6 +50,7 @@ Methods:
5050
- <code title="get /images">client.images.<a href="./src/hypeman/resources/images.py">list</a>(\*\*<a href="src/hypeman/types/image_list_params.py">params</a>) -> <a href="./src/hypeman/types/image_list_response.py">ImageListResponse</a></code>
5151
- <code title="delete /images/{name}">client.images.<a href="./src/hypeman/resources/images.py">delete</a>(name) -> None</code>
5252
- <code title="get /images/{name}">client.images.<a href="./src/hypeman/resources/images.py">get</a>(name) -> <a href="./src/hypeman/types/image.py">Image</a></code>
53+
- <code title="post /images/{name}/tag">client.images.<a href="./src/hypeman/resources/images.py">tag</a>(name, \*\*<a href="src/hypeman/types/image_tag_params.py">params</a>) -> <a href="./src/hypeman/types/image.py">Image</a></code>
5354

5455
# Instances
5556

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "hypeman"
3-
version = "0.27.0"
3+
version = "0.28.0"
44
description = "The official Python library for the hypeman API"
55
dynamic = ["readme"]
66
license = "Apache-2.0"

src/hypeman/_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__ = "hypeman"
4-
__version__ = "0.27.0" # x-release-please-version
4+
__version__ = "0.28.0" # x-release-please-version

src/hypeman/resources/images.py

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import httpx
88

9-
from ..types import image_list_params, image_create_params
9+
from ..types import image_tag_params, image_list_params, image_create_params
1010
from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given
1111
from .._utils import path_template, maybe_transform, async_maybe_transform
1212
from .._compat import cached_property
@@ -206,6 +206,44 @@ def get(
206206
cast_to=Image,
207207
)
208208

209+
def tag(
210+
self,
211+
name: str,
212+
*,
213+
target: str,
214+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
215+
# The extra values given here take precedence over values defined on the client or passed to this method.
216+
extra_headers: Headers | None = None,
217+
extra_query: Query | None = None,
218+
extra_body: Body | None = None,
219+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
220+
) -> Image:
221+
"""
222+
Create or update a local image tag
223+
224+
Args:
225+
target: Target OCI image reference with a tag. The local tag points to the source image
226+
without pulling it again.
227+
228+
extra_headers: Send extra headers
229+
230+
extra_query: Add additional query parameters to the request
231+
232+
extra_body: Add additional JSON properties to the request
233+
234+
timeout: Override the client-level default timeout for this request, in seconds
235+
"""
236+
if not name:
237+
raise ValueError(f"Expected a non-empty value for `name` but received {name!r}")
238+
return self._post(
239+
path_template("/images/{name}/tag", name=name),
240+
body=maybe_transform({"target": target}, image_tag_params.ImageTagParams),
241+
options=make_request_options(
242+
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
243+
),
244+
cast_to=Image,
245+
)
246+
209247

210248
class AsyncImagesResource(AsyncAPIResource):
211249
@cached_property
@@ -388,6 +426,44 @@ async def get(
388426
cast_to=Image,
389427
)
390428

429+
async def tag(
430+
self,
431+
name: str,
432+
*,
433+
target: str,
434+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
435+
# The extra values given here take precedence over values defined on the client or passed to this method.
436+
extra_headers: Headers | None = None,
437+
extra_query: Query | None = None,
438+
extra_body: Body | None = None,
439+
timeout: float | httpx.Timeout | None | NotGiven = not_given,
440+
) -> Image:
441+
"""
442+
Create or update a local image tag
443+
444+
Args:
445+
target: Target OCI image reference with a tag. The local tag points to the source image
446+
without pulling it again.
447+
448+
extra_headers: Send extra headers
449+
450+
extra_query: Add additional query parameters to the request
451+
452+
extra_body: Add additional JSON properties to the request
453+
454+
timeout: Override the client-level default timeout for this request, in seconds
455+
"""
456+
if not name:
457+
raise ValueError(f"Expected a non-empty value for `name` but received {name!r}")
458+
return await self._post(
459+
path_template("/images/{name}/tag", name=name),
460+
body=await async_maybe_transform({"target": target}, image_tag_params.ImageTagParams),
461+
options=make_request_options(
462+
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
463+
),
464+
cast_to=Image,
465+
)
466+
391467

392468
class ImagesResourceWithRawResponse:
393469
def __init__(self, images: ImagesResource) -> None:
@@ -405,6 +481,9 @@ def __init__(self, images: ImagesResource) -> None:
405481
self.get = to_raw_response_wrapper(
406482
images.get,
407483
)
484+
self.tag = to_raw_response_wrapper(
485+
images.tag,
486+
)
408487

409488

410489
class AsyncImagesResourceWithRawResponse:
@@ -423,6 +502,9 @@ def __init__(self, images: AsyncImagesResource) -> None:
423502
self.get = async_to_raw_response_wrapper(
424503
images.get,
425504
)
505+
self.tag = async_to_raw_response_wrapper(
506+
images.tag,
507+
)
426508

427509

428510
class ImagesResourceWithStreamingResponse:
@@ -441,6 +523,9 @@ def __init__(self, images: ImagesResource) -> None:
441523
self.get = to_streamed_response_wrapper(
442524
images.get,
443525
)
526+
self.tag = to_streamed_response_wrapper(
527+
images.tag,
528+
)
444529

445530

446531
class AsyncImagesResourceWithStreamingResponse:
@@ -459,3 +544,6 @@ def __init__(self, images: AsyncImagesResource) -> None:
459544
self.get = async_to_streamed_response_wrapper(
460545
images.get,
461546
)
547+
self.tag = async_to_streamed_response_wrapper(
548+
images.tag,
549+
)

src/hypeman/types/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from .available_device import AvailableDevice as AvailableDevice
3737
from .build_provenance import BuildProvenance as BuildProvenance
3838
from .health_check_tcp import HealthCheckTcp as HealthCheckTcp
39+
from .image_tag_params import ImageTagParams as ImageTagParams
3940
from .build_list_params import BuildListParams as BuildListParams
4041
from .capabilities_host import CapabilitiesHost as CapabilitiesHost
4142
from .health_check_exec import HealthCheckExec as HealthCheckExec
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2+
3+
from __future__ import annotations
4+
5+
from typing_extensions import Required, TypedDict
6+
7+
__all__ = ["ImageTagParams"]
8+
9+
10+
class ImageTagParams(TypedDict, total=False):
11+
target: Required[str]
12+
"""Target OCI image reference with a tag.
13+
14+
The local tag points to the source image without pulling it again.
15+
"""

tests/api_resources/test_images.py

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99

1010
from hypeman import Hypeman, AsyncHypeman
1111
from tests.utils import assert_matches_type
12-
from hypeman.types import Image, ImageListResponse
12+
from hypeman.types import (
13+
Image,
14+
ImageListResponse,
15+
)
1316

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

@@ -192,6 +195,52 @@ def test_path_params_get(self, client: Hypeman) -> None:
192195
"",
193196
)
194197

198+
@pytest.mark.skip(reason="Mock server tests are disabled")
199+
@parametrize
200+
def test_method_tag(self, client: Hypeman) -> None:
201+
image = client.images.tag(
202+
name="name",
203+
target="docker.io/library/nginx:stable",
204+
)
205+
assert_matches_type(Image, image, path=["response"])
206+
207+
@pytest.mark.skip(reason="Mock server tests are disabled")
208+
@parametrize
209+
def test_raw_response_tag(self, client: Hypeman) -> None:
210+
response = client.images.with_raw_response.tag(
211+
name="name",
212+
target="docker.io/library/nginx:stable",
213+
)
214+
215+
assert response.is_closed is True
216+
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
217+
image = response.parse()
218+
assert_matches_type(Image, image, path=["response"])
219+
220+
@pytest.mark.skip(reason="Mock server tests are disabled")
221+
@parametrize
222+
def test_streaming_response_tag(self, client: Hypeman) -> None:
223+
with client.images.with_streaming_response.tag(
224+
name="name",
225+
target="docker.io/library/nginx:stable",
226+
) as response:
227+
assert not response.is_closed
228+
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
229+
230+
image = response.parse()
231+
assert_matches_type(Image, image, path=["response"])
232+
233+
assert cast(Any, response.is_closed) is True
234+
235+
@pytest.mark.skip(reason="Mock server tests are disabled")
236+
@parametrize
237+
def test_path_params_tag(self, client: Hypeman) -> None:
238+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `name` but received ''"):
239+
client.images.with_raw_response.tag(
240+
name="",
241+
target="docker.io/library/nginx:stable",
242+
)
243+
195244

196245
class TestAsyncImages:
197246
parametrize = pytest.mark.parametrize(
@@ -372,3 +421,49 @@ async def test_path_params_get(self, async_client: AsyncHypeman) -> None:
372421
await async_client.images.with_raw_response.get(
373422
"",
374423
)
424+
425+
@pytest.mark.skip(reason="Mock server tests are disabled")
426+
@parametrize
427+
async def test_method_tag(self, async_client: AsyncHypeman) -> None:
428+
image = await async_client.images.tag(
429+
name="name",
430+
target="docker.io/library/nginx:stable",
431+
)
432+
assert_matches_type(Image, image, path=["response"])
433+
434+
@pytest.mark.skip(reason="Mock server tests are disabled")
435+
@parametrize
436+
async def test_raw_response_tag(self, async_client: AsyncHypeman) -> None:
437+
response = await async_client.images.with_raw_response.tag(
438+
name="name",
439+
target="docker.io/library/nginx:stable",
440+
)
441+
442+
assert response.is_closed is True
443+
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
444+
image = await response.parse()
445+
assert_matches_type(Image, image, path=["response"])
446+
447+
@pytest.mark.skip(reason="Mock server tests are disabled")
448+
@parametrize
449+
async def test_streaming_response_tag(self, async_client: AsyncHypeman) -> None:
450+
async with async_client.images.with_streaming_response.tag(
451+
name="name",
452+
target="docker.io/library/nginx:stable",
453+
) as response:
454+
assert not response.is_closed
455+
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
456+
457+
image = await response.parse()
458+
assert_matches_type(Image, image, path=["response"])
459+
460+
assert cast(Any, response.is_closed) is True
461+
462+
@pytest.mark.skip(reason="Mock server tests are disabled")
463+
@parametrize
464+
async def test_path_params_tag(self, async_client: AsyncHypeman) -> None:
465+
with pytest.raises(ValueError, match=r"Expected a non-empty value for `name` but received ''"):
466+
await async_client.images.with_raw_response.tag(
467+
name="",
468+
target="docker.io/library/nginx:stable",
469+
)

0 commit comments

Comments
 (0)