From 69fcbab85c3492e97402b804c3f9393fad65754b Mon Sep 17 00:00:00 2001 From: Tamir Date: Tue, 31 Mar 2026 11:30:01 +0300 Subject: [PATCH 1/3] added option to query images by instance_type, refactored to dataclasses --- verda/images/_images.py | 96 +++++++++++------------------------------ 1 file changed, 26 insertions(+), 70 deletions(-) diff --git a/verda/images/_images.py b/verda/images/_images.py index 1ed1e48..928126c 100644 --- a/verda/images/_images.py +++ b/verda/images/_images.py @@ -1,71 +1,26 @@ -from verda.helpers import stringify_class_object_properties +from dataclasses import dataclass + +from dataclasses_json import Undefined, dataclass_json IMAGES_ENDPOINT = '/images' +@dataclass_json(undefined=Undefined.EXCLUDE) +@dataclass class Image: - """An image model class.""" - - def __init__(self, id: str, name: str, image_type: str, details: list[str]) -> None: - """Initialize an image object. - - :param id: image id - :type id: str - :param name: image name - :type name: str - :param image_type: image type, e.g. 'ubuntu-20.04-cuda-11.0' - :type image_type: str - :param details: image details - :type details: list[str] - """ - self._id = id - self._name = name - self._image_type = image_type - self._details = details - - @property - def id(self) -> str: - """Get the image id. - - :return: image id - :rtype: str - """ - return self._id - - @property - def name(self) -> str: - """Get the image name. - - :return: image name - :rtype: str - """ - return self._name - - @property - def image_type(self) -> str: - """Get the image type. + """Represents an OS image available for instances. - :return: image type - :rtype: str - """ - return self._image_type + Attributes: + id: Unique identifier for the image. + name: Human-readable name of the image. + image_type: Image type identifier, e.g. 'ubuntu-20.04-cuda-11.0'. + details: List of additional image details. + """ - @property - def details(self) -> list[str]: - """Get the image details. - - :return: image details - :rtype: list[str] - """ - return self._details - - def __str__(self) -> str: - """Returns a string of the json representation of the image. - - :return: json representation of the image - :rtype: str - """ - return stringify_class_object_properties(self) + id: str + name: str + image_type: str + details: list[str] class ImagesService: @@ -74,15 +29,16 @@ class ImagesService: def __init__(self, http_client) -> None: self._http_client = http_client - def get(self) -> list[Image]: + def get(self, instance_type: str | None = None) -> list[Image]: """Get the available instance images. - :return: list of images objects - :rtype: list[Image] + Args: + instance_type: Filter OS images by instance type, e.g. '1A100.22V'. + Default is all instance images. + + Returns: + List of Image objects. """ - images = self._http_client.get(IMAGES_ENDPOINT).json() - image_objects = [ - Image(image['id'], image['name'], image['image_type'], image['details']) - for image in images - ] - return image_objects + params = {'instance_type': instance_type} if instance_type else None + images = self._http_client.get(IMAGES_ENDPOINT, params=params).json() + return [Image.from_dict(image) for image in images] From f7c1443bcf33c88c6648d58c57c5db332700d1b9 Mon Sep 17 00:00:00 2001 From: Tamir Date: Tue, 31 Mar 2026 11:37:30 +0300 Subject: [PATCH 2/3] changelog entry and test --- CHANGELOG.md | 8 +++++ tests/unit_tests/images/test_images.py | 43 ++++++++++++++++++++------ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64d9c68..824e7ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Support for querying OS images by instance type via `verda.images.get(instance_type=...)` + +### Changed + +- Refactored `Image` model to use `@dataclass` and `@dataclass_json` for consistency with `Instance` and `Volume` + ## [1.24.0] - 2026-03-30 ### Added diff --git a/tests/unit_tests/images/test_images.py b/tests/unit_tests/images/test_images.py index a5e9828..e9feac7 100644 --- a/tests/unit_tests/images/test_images.py +++ b/tests/unit_tests/images/test_images.py @@ -1,21 +1,22 @@ import responses # https://github.com/getsentry/responses +from responses import matchers from verda.images import Image, ImagesService +IMAGE_RESPONSE = { + 'id': '0888da25-bb0d-41cc-a191-dccae45d96fd', + 'name': 'Ubuntu 20.04 + CUDA 11.0', + 'details': ['Ubuntu 20.04', 'CUDA 11.0'], + 'image_type': 'ubuntu-20.04-cuda-11.0', +} + def test_images(http_client): - # arrange - add response mock + # arrange responses.add( responses.GET, http_client._base_url + '/images', - json=[ - { - 'id': '0888da25-bb0d-41cc-a191-dccae45d96fd', - 'name': 'Ubuntu 20.04 + CUDA 11.0', - 'details': ['Ubuntu 20.04', 'CUDA 11.0'], - 'image_type': 'ubuntu-20.04-cuda-11.0', - } - ], + json=[IMAGE_RESPONSE], status=200, ) @@ -34,4 +35,26 @@ def test_images(http_client): assert isinstance(images[0].details, list) assert images[0].details[0] == 'Ubuntu 20.04' assert images[0].details[1] == 'CUDA 11.0' - assert isinstance(images[0].__str__(), str) + + +def test_images_filter_by_instance_type(http_client): + # arrange + responses.add( + responses.GET, + http_client._base_url + '/images', + match=[matchers.query_param_matcher({'instance_type': '1A100.22V'})], + json=[IMAGE_RESPONSE], + status=200, + ) + + image_service = ImagesService(http_client) + + # act + images = image_service.get(instance_type='1A100.22V') + + # assert + assert isinstance(images, list) + assert len(images) == 1 + assert isinstance(images[0], Image) + assert images[0].id == '0888da25-bb0d-41cc-a191-dccae45d96fd' + assert images[0].image_type == 'ubuntu-20.04-cuda-11.0' From 1691706be488cb4e3c4b7ad626350960c5d7d650 Mon Sep 17 00:00:00 2001 From: Tamir Date: Tue, 31 Mar 2026 13:59:49 +0300 Subject: [PATCH 3/3] reimplement __str__ which was removed during refactor --- tests/unit_tests/images/test_images.py | 3 +++ verda/images/_images.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tests/unit_tests/images/test_images.py b/tests/unit_tests/images/test_images.py index e9feac7..2d1ed33 100644 --- a/tests/unit_tests/images/test_images.py +++ b/tests/unit_tests/images/test_images.py @@ -1,3 +1,5 @@ +import json + import responses # https://github.com/getsentry/responses from responses import matchers @@ -35,6 +37,7 @@ def test_images(http_client): assert isinstance(images[0].details, list) assert images[0].details[0] == 'Ubuntu 20.04' assert images[0].details[1] == 'CUDA 11.0' + assert json.loads(str(images[0])) == IMAGE_RESPONSE def test_images_filter_by_instance_type(http_client): diff --git a/verda/images/_images.py b/verda/images/_images.py index 928126c..a7f2129 100644 --- a/verda/images/_images.py +++ b/verda/images/_images.py @@ -22,6 +22,9 @@ class Image: image_type: str details: list[str] + def __str__(self) -> str: + return self.to_json(indent=2) + class ImagesService: """A service for interacting with the images endpoint."""