Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-hcloud for openSUSE:Factory checked in at 2026-04-25 21:38:07 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-hcloud (Old) and /work/SRC/openSUSE:Factory/.python-hcloud.new.11940 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-hcloud" Sat Apr 25 21:38:07 2026 rev:18 rq:1349221 version:2.18.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-hcloud/python-hcloud.changes 2026-03-27 06:47:54.106002995 +0100 +++ /work/SRC/openSUSE:Factory/.python-hcloud.new.11940/python-hcloud.changes 2026-04-25 21:43:03.135709369 +0200 @@ -1,0 +2,10 @@ +Sat Apr 25 10:27:07 UTC 2026 - Dirk Müller <[email protected]> + +- update to 2.18.0: + * `Datacenter.server_types` has been deprecated in favor of the + new `ServerType.locations[].available` and + `ServerType.locations[].recommended` properties. + * **datacenter, server_type**: move available and recommended + to server_type + +------------------------------------------------------------------- Old: ---- hcloud-2.17.1.tar.gz New: ---- hcloud-2.18.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-hcloud.spec ++++++ --- /var/tmp/diff_new_pack.9f8UBe/_old 2026-04-25 21:43:03.595728137 +0200 +++ /var/tmp/diff_new_pack.9f8UBe/_new 2026-04-25 21:43:03.595728137 +0200 @@ -18,13 +18,13 @@ %{?sle15_python_module_pythons} Name: python-hcloud -Version: 2.17.1 +Version: 2.18.0 Release: 0 Summary: Hetzner Cloud Python library License: MIT URL: https://github.com/hetznercloud/hcloud-python Source: https://files.pythonhosted.org/packages/source/h/hcloud/hcloud-%{version}.tar.gz -BuildRequires: %{python_module base >= 3.9} +BuildRequires: %{python_module base >= 3.10} BuildRequires: %{python_module pip} BuildRequires: %{python_module setuptools} BuildRequires: %{python_module wheel} @@ -34,7 +34,7 @@ Requires: python-requests >= 2.20 BuildArch: noarch # SECTION test requirements -BuildRequires: %{python_module pytest} +BuildRequires: %{python_module pytest >= 9} BuildRequires: %{python_module python-dateutil >= 2.7.5} BuildRequires: %{python_module requests >= 2.20} # /SECTION ++++++ hcloud-2.17.1.tar.gz -> hcloud-2.18.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-2.17.1/CHANGELOG.md new/hcloud-2.18.0/CHANGELOG.md --- old/hcloud-2.17.1/CHANGELOG.md 2026-03-23 11:50:49.000000000 +0100 +++ new/hcloud-2.18.0/CHANGELOG.md 2026-04-23 12:03:05.000000000 +0200 @@ -1,5 +1,17 @@ # Changelog +## [v2.18.0](https://github.com/hetznercloud/hcloud-python/releases/tag/v2.18.0) + +### Available and recommended Server Types have been moved + +`Datacenter.server_types` has been deprecated in favor of the new `ServerType.locations[].available` and `ServerType.locations[].recommended` properties. + +See the [changelog](https://docs.hetzner.cloud/changelog#2026-04-01-datacenter-deprecations) for more details. + +### Features + +- **datacenter, server_type**: move available and recommended to server_type (#645) + ## [v2.17.1](https://github.com/hetznercloud/hcloud-python/releases/tag/v2.17.1) ### Bug Fixes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-2.17.1/PKG-INFO new/hcloud-2.18.0/PKG-INFO --- old/hcloud-2.17.1/PKG-INFO 2026-03-23 11:50:58.227921200 +0100 +++ new/hcloud-2.18.0/PKG-INFO 2026-04-23 12:03:14.926858000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: hcloud -Version: 2.17.1 +Version: 2.18.0 Summary: Official Hetzner Cloud python library Home-page: https://github.com/hetznercloud/hcloud-python Author: Hetzner Cloud GmbH @@ -35,7 +35,7 @@ Requires-Dist: pylint<4.1,>=4; extra == "test" Requires-Dist: pytest<9.1,>=9; extra == "test" Requires-Dist: pytest-cov<7.2,>=7; extra == "test" -Requires-Dist: mypy<1.20,>=1.19; extra == "test" +Requires-Dist: mypy<1.21,>=1.20; extra == "test" Requires-Dist: types-python-dateutil; extra == "test" Requires-Dist: types-requests; extra == "test" Dynamic: author diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-2.17.1/hcloud/_version.py new/hcloud-2.18.0/hcloud/_version.py --- old/hcloud-2.17.1/hcloud/_version.py 2026-03-23 11:50:49.000000000 +0100 +++ new/hcloud-2.18.0/hcloud/_version.py 2026-04-23 12:03:05.000000000 +0200 @@ -1,3 +1,3 @@ from __future__ import annotations -__version__ = "2.17.1" # x-releaser-pleaser-version +__version__ = "2.18.0" # x-releaser-pleaser-version diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-2.17.1/hcloud/datacenters/domain.py new/hcloud-2.18.0/hcloud/datacenters/domain.py --- old/hcloud-2.17.1/hcloud/datacenters/domain.py 2026-03-23 11:50:49.000000000 +0100 +++ new/hcloud-2.18.0/hcloud/datacenters/domain.py 2026-04-23 12:03:05.000000000 +0200 @@ -1,5 +1,6 @@ from __future__ import annotations +import warnings from typing import TYPE_CHECKING from ..core import BaseDomain, DomainIdentityMixin @@ -24,8 +25,9 @@ :param server_types: :class:`DatacenterServerTypes <hcloud.datacenters.domain.DatacenterServerTypes>` """ - __api_properties__ = ("id", "name", "description", "location", "server_types") - __slots__ = __api_properties__ + __properties__ = ("id", "name", "description", "location") + __api_properties__ = (*__properties__, "server_types") + __slots__ = (*__properties__, "_server_types") def __init__( self, @@ -39,7 +41,29 @@ self.name = name self.description = description self.location = location - self.server_types = server_types + self._server_types = server_types + + @property + def server_types(self) -> DatacenterServerTypes | None: + """ + .. deprecated:: 2.18.0 + The 'server_types' property is deprecated and will not be supported after 2026-10-01. + Please use 'server_type.locations[]' instead. + + See https://docs.hetzner.cloud/changelog#2026-04-01-datacenter-deprecations. + """ + warnings.warn( + "The 'server_types' property is deprecated and will not be supported after 2026-10-01. " + "Please use 'server_type.locations[]' instead. " + "See https://docs.hetzner.cloud/changelog#2026-04-01-datacenter-deprecations.", + DeprecationWarning, + stacklevel=2, + ) + return self._server_types + + @server_types.setter + def server_types(self, value: DatacenterServerTypes | None) -> None: + self._server_types = value class DatacenterServerTypes(BaseDomain): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-2.17.1/hcloud/server_types/domain.py new/hcloud-2.18.0/hcloud/server_types/domain.py --- old/hcloud-2.17.1/hcloud/server_types/domain.py 2026-03-23 11:50:49.000000000 +0100 +++ new/hcloud-2.18.0/hcloud/server_types/domain.py 2026-04-23 12:03:05.000000000 +0200 @@ -185,11 +185,15 @@ :param location: Location of the Server Type. :param deprecation: Wether the Server Type is deprecated in this Location. + :param available: Whether the Server Type is currently available in this Location. + :param recommended: Whether the Server Type is currently recommended in this Location. """ __api_properties__ = ( "location", "deprecation", + "available", + "recommended", ) __slots__ = __api_properties__ @@ -198,8 +202,12 @@ *, location: BoundLocation, deprecation: dict[str, Any] | None, + available: bool | None, + recommended: bool | None, ): self.location = location self.deprecation = ( DeprecationInfo.from_dict(deprecation) if deprecation is not None else None ) + self.available = available + self.recommended = recommended diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-2.17.1/hcloud.egg-info/PKG-INFO new/hcloud-2.18.0/hcloud.egg-info/PKG-INFO --- old/hcloud-2.17.1/hcloud.egg-info/PKG-INFO 2026-03-23 11:50:58.000000000 +0100 +++ new/hcloud-2.18.0/hcloud.egg-info/PKG-INFO 2026-04-23 12:03:14.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: hcloud -Version: 2.17.1 +Version: 2.18.0 Summary: Official Hetzner Cloud python library Home-page: https://github.com/hetznercloud/hcloud-python Author: Hetzner Cloud GmbH @@ -35,7 +35,7 @@ Requires-Dist: pylint<4.1,>=4; extra == "test" Requires-Dist: pytest<9.1,>=9; extra == "test" Requires-Dist: pytest-cov<7.2,>=7; extra == "test" -Requires-Dist: mypy<1.20,>=1.19; extra == "test" +Requires-Dist: mypy<1.21,>=1.20; extra == "test" Requires-Dist: types-python-dateutil; extra == "test" Requires-Dist: types-requests; extra == "test" Dynamic: author diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-2.17.1/hcloud.egg-info/requires.txt new/hcloud-2.18.0/hcloud.egg-info/requires.txt --- old/hcloud-2.17.1/hcloud.egg-info/requires.txt 2026-03-23 11:50:58.000000000 +0100 +++ new/hcloud-2.18.0/hcloud.egg-info/requires.txt 2026-04-23 12:03:14.000000000 +0200 @@ -12,6 +12,6 @@ pylint<4.1,>=4 pytest<9.1,>=9 pytest-cov<7.2,>=7 -mypy<1.20,>=1.19 +mypy<1.21,>=1.20 types-python-dateutil types-requests diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-2.17.1/setup.py new/hcloud-2.18.0/setup.py --- old/hcloud-2.17.1/setup.py 2026-03-23 11:50:49.000000000 +0100 +++ new/hcloud-2.18.0/setup.py 2026-04-23 12:03:05.000000000 +0200 @@ -7,7 +7,7 @@ setup( name="hcloud", - version="2.17.1", # x-releaser-pleaser-version + version="2.18.0", # x-releaser-pleaser-version keywords="hcloud hetzner cloud", description="Official Hetzner Cloud python library", long_description=readme, @@ -50,7 +50,7 @@ "pylint>=4,<4.1", "pytest>=9,<9.1", "pytest-cov>=7,<7.2", - "mypy>=1.19,<1.20", + "mypy>=1.20,<1.21", "types-python-dateutil", "types-requests", ], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-2.17.1/tests/unit/datacenters/test_client.py new/hcloud-2.18.0/tests/unit/datacenters/test_client.py --- old/hcloud-2.17.1/tests/unit/datacenters/test_client.py 2026-03-23 11:50:49.000000000 +0100 +++ new/hcloud-2.18.0/tests/unit/datacenters/test_client.py 2026-04-23 12:03:05.000000000 +0200 @@ -25,36 +25,40 @@ assert bound_datacenter.location.name == "fsn1" assert bound_datacenter.location.complete is True - assert isinstance(bound_datacenter.server_types, DatacenterServerTypes) - assert len(bound_datacenter.server_types.supported) == 3 - assert bound_datacenter.server_types.supported[0].id == 1 - assert bound_datacenter.server_types.supported[0].complete is False - assert bound_datacenter.server_types.supported[1].id == 2 - assert bound_datacenter.server_types.supported[1].complete is False - assert bound_datacenter.server_types.supported[2].id == 3 - assert bound_datacenter.server_types.supported[2].complete is False + with pytest.deprecated_call(): + assert isinstance(bound_datacenter.server_types, DatacenterServerTypes) + assert len(bound_datacenter.server_types.supported) == 3 + assert bound_datacenter.server_types.supported[0].id == 1 + assert bound_datacenter.server_types.supported[0].complete is False + assert bound_datacenter.server_types.supported[1].id == 2 + assert bound_datacenter.server_types.supported[1].complete is False + assert bound_datacenter.server_types.supported[2].id == 3 + assert bound_datacenter.server_types.supported[2].complete is False - assert len(bound_datacenter.server_types.available) == 3 - assert bound_datacenter.server_types.available[0].id == 1 - assert bound_datacenter.server_types.available[0].complete is False - assert bound_datacenter.server_types.available[1].id == 2 - assert bound_datacenter.server_types.available[1].complete is False - assert bound_datacenter.server_types.available[2].id == 3 - assert bound_datacenter.server_types.available[2].complete is False + assert len(bound_datacenter.server_types.available) == 3 + assert bound_datacenter.server_types.available[0].id == 1 + assert bound_datacenter.server_types.available[0].complete is False + assert bound_datacenter.server_types.available[1].id == 2 + assert bound_datacenter.server_types.available[1].complete is False + assert bound_datacenter.server_types.available[2].id == 3 + assert bound_datacenter.server_types.available[2].complete is False - assert len(bound_datacenter.server_types.available_for_migration) == 3 - assert bound_datacenter.server_types.available_for_migration[0].id == 1 - assert ( - bound_datacenter.server_types.available_for_migration[0].complete is False - ) - assert bound_datacenter.server_types.available_for_migration[1].id == 2 - assert ( - bound_datacenter.server_types.available_for_migration[1].complete is False - ) - assert bound_datacenter.server_types.available_for_migration[2].id == 3 - assert ( - bound_datacenter.server_types.available_for_migration[2].complete is False - ) + assert len(bound_datacenter.server_types.available_for_migration) == 3 + assert bound_datacenter.server_types.available_for_migration[0].id == 1 + assert ( + bound_datacenter.server_types.available_for_migration[0].complete + is False + ) + assert bound_datacenter.server_types.available_for_migration[1].id == 2 + assert ( + bound_datacenter.server_types.available_for_migration[1].complete + is False + ) + assert bound_datacenter.server_types.available_for_migration[2].id == 3 + assert ( + bound_datacenter.server_types.available_for_migration[2].complete + is False + ) class TestDatacentersClient: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-2.17.1/tests/unit/primary_ips/conftest.py new/hcloud-2.18.0/tests/unit/primary_ips/conftest.py --- old/hcloud-2.17.1/tests/unit/primary_ips/conftest.py 2026-03-23 11:50:49.000000000 +0100 +++ new/hcloud-2.18.0/tests/unit/primary_ips/conftest.py 2026-04-23 12:03:05.000000000 +0200 @@ -4,252 +4,56 @@ @pytest.fixture() -def primary_ip_response(): +def primary_ip1(): return { - "primary_ip": { - "assignee_id": 17, - "assignee_type": "server", - "auto_delete": True, - "blocked": False, - "created": "2016-01-30T23:55:00+00:00", - "datacenter": { - "description": "Falkenstein DC Park 8", - "id": 42, - "location": { - "city": "Falkenstein", - "country": "DE", - "description": "Falkenstein DC Park 1", - "id": 1, - "latitude": 50.47612, - "longitude": 12.370071, - "name": "fsn1", - "network_zone": "eu-central", - }, - "name": "fsn1-dc8", - "server_types": { - "available": [1, 2, 3], - "available_for_migration": [1, 2, 3], - "supported": [1, 2, 3], - }, - }, - "dns_ptr": [{"dns_ptr": "server.example.com", "ip": "131.232.99.1"}], - "id": 42, - "ip": "131.232.99.1", - "labels": {}, - "name": "my-resource", - "protection": {"delete": False}, - "type": "ipv4", - } - } - - [email protected]() -def one_primary_ips_response(): - return { - "meta": { - "pagination": { - "last_page": 4, - "next_page": 4, - "page": 3, - "per_page": 25, - "previous_page": 2, - "total_entries": 100, - } + "id": 42, + "name": "primary-ip1", + "type": "ipv4", + "ip": "131.232.99.1", + "assignee_id": 17, + "assignee_type": "server", + "auto_delete": True, + "blocked": False, + "datacenter": { + "id": 4, + "name": "fsn1-dc14", }, - "primary_ips": [ - { - "assignee_id": 17, - "assignee_type": "server", - "auto_delete": True, - "blocked": False, - "created": "2016-01-30T23:55:00+00:00", - "datacenter": { - "description": "Falkenstein DC Park 8", - "id": 42, - "location": { - "city": "Falkenstein", - "country": "DE", - "description": "Falkenstein DC Park 1", - "id": 1, - "latitude": 50.47612, - "longitude": 12.370071, - "name": "fsn1", - "network_zone": "eu-central", - }, - "name": "fsn1-dc8", - "server_types": { - "available": [1, 2, 3], - "available_for_migration": [1, 2, 3], - "supported": [1, 2, 3], - }, - }, - "dns_ptr": [{"dns_ptr": "server.example.com", "ip": "131.232.99.1"}], - "id": 42, - "ip": "131.232.99.1", - "labels": {}, - "name": "my-resource", - "protection": {"delete": False}, - "type": "ipv4", - } - ], - } - - [email protected]() -def all_primary_ips_response(): - return { - "meta": { - "pagination": { - "last_page": 1, - "next_page": None, - "page": 1, - "per_page": 25, - "previous_page": None, - "total_entries": 1, - } + "location": { + "id": 1, + "name": "fsn1", }, - "primary_ips": [ - { - "assignee_id": 17, - "assignee_type": "server", - "auto_delete": True, - "blocked": False, - "created": "2016-01-30T23:55:00+00:00", - "datacenter": { - "description": "Falkenstein DC Park 8", - "id": 42, - "location": { - "city": "Falkenstein", - "country": "DE", - "description": "Falkenstein DC Park 1", - "id": 1, - "latitude": 50.47612, - "longitude": 12.370071, - "name": "fsn1", - "network_zone": "eu-central", - }, - "name": "fsn1-dc8", - "server_types": { - "available": [1, 2, 3], - "available_for_migration": [1, 2, 3], - "supported": [1, 2, 3], - }, - }, - "dns_ptr": [{"dns_ptr": "server.example.com", "ip": "131.232.99.1"}], - "id": 42, - "ip": "131.232.99.1", - "labels": {}, - "name": "my-resource", - "protection": {"delete": False}, - "type": "ipv4", - } + "dns_ptr": [ + {"dns_ptr": "server.example.com", "ip": "131.232.99.1"}, ], + "labels": {"key": "value"}, + "protection": {"delete": False}, + "created": "2016-01-30T23:55:00Z", } @pytest.fixture() -def primary_ip_create_response(): - return { - "action": { - "command": "create_primary_ip", - "error": {"code": "action_failed", "message": "Action failed"}, - "finished": None, - "id": 13, - "progress": 0, - "resources": [{"id": 17, "type": "server"}], - "started": "2016-01-30T23:50:00+00:00", - "status": "running", +def primary_ip2(): + return { + "id": 52, + "name": "primary-ip2", + "type": "ipv4", + "ip": "131.232.99.2", + "assignee_id": None, + "assignee_type": "server", + "auto_delete": True, + "blocked": False, + "datacenter": { + "id": 4, + "name": "fsn1-dc14", }, - "primary_ip": { - "assignee_id": 17, - "assignee_type": "server", - "auto_delete": True, - "blocked": False, - "created": "2016-01-30T23:50:00+00:00", - "datacenter": { - "description": "Falkenstein DC Park 8", - "id": 42, - "location": { - "city": "Falkenstein", - "country": "DE", - "description": "Falkenstein DC Park 1", - "id": 1, - "latitude": 50.47612, - "longitude": 12.370071, - "name": "fsn1", - "network_zone": "eu-central", - "server_types": { - "available": [1, 2, 3], - "available_for_migration": [1, 2, 3], - "supported": [1, 2, 3], - }, - }, - "name": "fsn1-dc8", - }, - "dns_ptr": [{"dns_ptr": "server.example.com", "ip": "2001:db8::1"}], - "id": 42, - "ip": "131.232.99.1", - "labels": {"labelkey": "value"}, - "name": "my-ip", - "protection": {"delete": False}, - "type": "ipv4", + "location": { + "id": 1, + "name": "fsn1", }, - } - - [email protected]() -def response_update_primary_ip(): - return { - "primary_ip": { - "assignee_id": 17, - "assignee_type": "server", - "auto_delete": True, - "blocked": False, - "created": "2016-01-30T23:55:00+00:00", - "datacenter": { - "description": "Falkenstein DC Park 8", - "id": 42, - "location": { - "city": "Falkenstein", - "country": "DE", - "description": "Falkenstein DC Park 1", - "id": 1, - "latitude": 50.47612, - "longitude": 12.370071, - "name": "fsn1", - "network_zone": "eu-central", - }, - "name": "fsn1-dc8", - "server_types": { - "available": [1, 2, 3], - "available_for_migration": [1, 2, 3], - "supported": [1, 2, 3], - }, - }, - "dns_ptr": [{"dns_ptr": "server.example.com", "ip": "131.232.99.1"}], - "id": 42, - "ip": "131.232.99.1", - "labels": {}, - "name": "my-resource", - "protection": {"delete": False}, - "type": "ipv4", - } - } - - [email protected]() -def response_get_actions(): - return { - "actions": [ - { - "id": 13, - "command": "assign_primary_ip", - "status": "success", - "progress": 100, - "started": "2016-01-30T23:55:00+00:00", - "finished": "2016-01-30T23:56:00+00:00", - "resources": [{"id": 42, "type": "server"}], - "error": {"code": "action_failed", "message": "Action failed"}, - } - ] + "dns_ptr": [ + {"dns_ptr": "server.example.com", "ip": "131.232.99.1"}, + ], + "labels": {"key": "value"}, + "protection": {"delete": False}, + "created": "2016-01-30T23:55:00Z", } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-2.17.1/tests/unit/primary_ips/test_client.py new/hcloud-2.18.0/tests/unit/primary_ips/test_client.py --- old/hcloud-2.17.1/tests/unit/primary_ips/test_client.py 2026-03-23 11:50:49.000000000 +0100 +++ new/hcloud-2.18.0/tests/unit/primary_ips/test_client.py 2026-04-23 12:03:05.000000000 +0200 @@ -6,9 +6,24 @@ from hcloud import Client from hcloud.datacenters import BoundDatacenter, Datacenter +from hcloud.locations import BoundLocation, Location from hcloud.primary_ips import BoundPrimaryIP, PrimaryIP, PrimaryIPsClient -from ..conftest import BoundModelTestCase +from ..conftest import BoundModelTestCase, assert_bound_action1 + + +def assert_bound_primary_ip1(o: BoundPrimaryIP, client: PrimaryIPsClient): + assert isinstance(o, BoundPrimaryIP) + assert o._client is client + assert o.id == 42 + assert o.name == "primary-ip1" + + +def assert_bound_primary_ip2(o: BoundPrimaryIP, client: PrimaryIPsClient): + assert isinstance(o, BoundPrimaryIP) + assert o._client is client + assert o.id == 52 + assert o.name == "primary-ip2" class TestBoundPrimaryIP(BoundModelTestCase): @@ -29,87 +44,88 @@ def bound_model(self, resource_client: PrimaryIPsClient): return BoundPrimaryIP(resource_client, data=dict(id=14)) - def test_init(self, primary_ip_response): - bound_primary_ip = BoundPrimaryIP( - client=mock.MagicMock(), data=primary_ip_response["primary_ip"] - ) + def test_init(self, primary_ip1): + o = BoundPrimaryIP(client=mock.MagicMock(), data=primary_ip1) - assert bound_primary_ip.id == 42 - assert bound_primary_ip.name == "my-resource" - assert bound_primary_ip.ip == "131.232.99.1" - assert bound_primary_ip.type == "ipv4" - assert bound_primary_ip.protection == {"delete": False} - assert bound_primary_ip.labels == {} - assert bound_primary_ip.blocked is False - - assert bound_primary_ip.assignee_id == 17 - assert bound_primary_ip.assignee_type == "server" + assert o.id == 42 + assert o.name == "primary-ip1" + assert o.ip == "131.232.99.1" + assert o.type == "ipv4" + assert o.protection == {"delete": False} + assert o.labels == {"key": "value"} + assert o.blocked is False + + assert o.assignee_id == 17 + assert o.assignee_type == "server" + + assert isinstance(o.location, BoundLocation) + assert o.location.id == 1 + assert o.location.name == "fsn1" with pytest.deprecated_call(): - datacenter = bound_primary_ip.datacenter + datacenter = o.datacenter assert isinstance(datacenter, BoundDatacenter) - assert datacenter.id == 42 - assert datacenter.name == "fsn1-dc8" - assert datacenter.description == "Falkenstein DC Park 8" - assert datacenter.location.country == "DE" - assert datacenter.location.city == "Falkenstein" - assert datacenter.location.latitude == 50.47612 - assert datacenter.location.longitude == 12.370071 + assert datacenter.id == 4 + assert datacenter.name == "fsn1-dc14" class TestPrimaryIPsClient: @pytest.fixture() - def primary_ips_client(self, client: Client): + def resource_client(self, client: Client): return PrimaryIPsClient(client) def test_get_by_id( self, request_mock: mock.MagicMock, - primary_ips_client: PrimaryIPsClient, - primary_ip_response, + resource_client: PrimaryIPsClient, + primary_ip1, ): - request_mock.return_value = primary_ip_response + request_mock.return_value = {"primary_ip": primary_ip1} - bound_primary_ip = primary_ips_client.get_by_id(1) + result = resource_client.get_by_id(1) request_mock.assert_called_with( method="GET", url="/primary_ips/1", ) - assert bound_primary_ip._client is primary_ips_client - assert bound_primary_ip.id == 42 + assert_bound_primary_ip1(result, resource_client) def test_get_by_name( self, request_mock: mock.MagicMock, - primary_ips_client: PrimaryIPsClient, - one_primary_ips_response, + resource_client: PrimaryIPsClient, + primary_ip1, ): - request_mock.return_value = one_primary_ips_response + request_mock.return_value = {"primary_ips": [primary_ip1]} - bound_primary_ip = primary_ips_client.get_by_name("my-resource") + result = resource_client.get_by_name("primary-ip1") request_mock.assert_called_with( method="GET", url="/primary_ips", - params={"name": "my-resource"}, + params={"name": "primary-ip1"}, ) - assert bound_primary_ip._client is primary_ips_client - assert bound_primary_ip.id == 42 - assert bound_primary_ip.name == "my-resource" + assert_bound_primary_ip1(result, resource_client) - @pytest.mark.parametrize("params", [{"label_selector": "label1"}]) + @pytest.mark.parametrize( + "params", + [ + {"name": "primary-ip1"}, + {"label_selector": "key=value"}, + ], + ) def test_get_all( self, request_mock: mock.MagicMock, - primary_ips_client: PrimaryIPsClient, - all_primary_ips_response, + resource_client: PrimaryIPsClient, + primary_ip1, + primary_ip2, params, ): - request_mock.return_value = all_primary_ips_response + request_mock.return_value = {"primary_ips": [primary_ip1, primary_ip2]} - bound_primary_ips = primary_ips_client.get_all(**params) + result = resource_client.get_all(**params) params.update({"page": 1, "per_page": 50}) @@ -119,60 +135,88 @@ params=params, ) - assert len(bound_primary_ips) == 1 + assert len(result) == 2 + assert_bound_primary_ip1(result[0], resource_client) + assert_bound_primary_ip2(result[1], resource_client) - bound_primary_ip1 = bound_primary_ips[0] + def test_create_with_location( + self, + request_mock: mock.MagicMock, + resource_client: PrimaryIPsClient, + primary_ip1, + ): + request_mock.return_value = { + "primary_ip": primary_ip1, + "action": None, + } - assert bound_primary_ip1._client is primary_ips_client - assert bound_primary_ip1.id == 42 - assert bound_primary_ip1.name == "my-resource" + result = resource_client.create( + type="ipv4", + name="primary-ip1", + location=Location(name="fsn1"), + ) + + request_mock.assert_called_with( + method="POST", + url="/primary_ips", + json={ + "name": "primary-ip1", + "type": "ipv4", + "assignee_type": "server", + "location": "fsn1", + "auto_delete": False, + }, + ) + assert_bound_primary_ip1(result.primary_ip, resource_client) + assert result.action is None def test_create_with_datacenter( self, request_mock: mock.MagicMock, - primary_ips_client: PrimaryIPsClient, - primary_ip_response, + resource_client: PrimaryIPsClient, + primary_ip1, ): - request_mock.return_value = primary_ip_response + request_mock.return_value = { + "primary_ip": primary_ip1, + "action": None, + } with pytest.deprecated_call(): - response = primary_ips_client.create( - type="ipv6", - name="my-resource", - datacenter=Datacenter(name="datacenter"), + result = resource_client.create( + type="ipv4", + name="primary-ip1", + datacenter=Datacenter(name="fsn1-dc14"), ) request_mock.assert_called_with( method="POST", url="/primary_ips", json={ - "name": "my-resource", - "type": "ipv6", + "name": "primary-ip1", + "type": "ipv4", "assignee_type": "server", - "datacenter": "datacenter", + "datacenter": "fsn1-dc14", "auto_delete": False, }, ) - - bound_primary_ip = response.primary_ip - action = response.action - - assert bound_primary_ip._client is primary_ips_client - assert bound_primary_ip.id == 42 - assert bound_primary_ip.name == "my-resource" - assert action is None + assert_bound_primary_ip1(result.primary_ip, resource_client) + assert result.action is None def test_create_with_assignee_id( self, request_mock: mock.MagicMock, - primary_ips_client: PrimaryIPsClient, - primary_ip_create_response, - ): - request_mock.return_value = primary_ip_create_response - - response = primary_ips_client.create( - type="ipv6", - name="my-ip", + resource_client: PrimaryIPsClient, + primary_ip1, + action1_running, + ): + request_mock.return_value = { + "primary_ip": primary_ip1, + "action": action1_running, + } + + result = resource_client.create( + type="ipv4", + name="primary-ip1", assignee_id=17, assignee_type="server", ) @@ -181,161 +225,187 @@ method="POST", url="/primary_ips", json={ - "name": "my-ip", - "type": "ipv6", + "name": "primary-ip1", + "type": "ipv4", "assignee_id": 17, "assignee_type": "server", "auto_delete": False, }, ) - bound_primary_ip = response.primary_ip - action = response.action - assert bound_primary_ip._client is primary_ips_client - assert bound_primary_ip.id == 42 - assert bound_primary_ip.name == "my-ip" - assert bound_primary_ip.assignee_id == 17 - assert action.id == 13 + assert_bound_primary_ip1(result.primary_ip, resource_client) + assert_bound_action1(result.action, resource_client._parent.actions) @pytest.mark.parametrize( - "primary_ip", [PrimaryIP(id=1), BoundPrimaryIP(mock.MagicMock(), dict(id=1))] + "primary_ip", + [ + PrimaryIP(id=42), + BoundPrimaryIP(client=mock.MagicMock(), data={"id": 42}), + ], ) def test_update( self, request_mock: mock.MagicMock, - primary_ips_client: PrimaryIPsClient, + resource_client: PrimaryIPsClient, primary_ip, - response_update_primary_ip, + primary_ip1, ): - request_mock.return_value = response_update_primary_ip + request_mock.return_value = {"primary_ip": primary_ip1} - primary_ip = primary_ips_client.update( - primary_ip, auto_delete=True, name="my-resource" + result = resource_client.update( + primary_ip, + name="changed", + auto_delete=True, ) request_mock.assert_called_with( method="PUT", - url="/primary_ips/1", - json={"auto_delete": True, "name": "my-resource"}, + url="/primary_ips/42", + json={ + "name": "changed", + "auto_delete": True, + }, ) - - assert primary_ip.id == 42 - assert primary_ip.auto_delete is True - assert primary_ip.name == "my-resource" + assert_bound_primary_ip1(result, resource_client) @pytest.mark.parametrize( - "primary_ip", [PrimaryIP(id=1), BoundPrimaryIP(mock.MagicMock(), dict(id=1))] + "primary_ip", + [ + PrimaryIP(id=42), + BoundPrimaryIP(client=mock.MagicMock(), data={"id": 42}), + ], ) - def test_change_protection( + def test_delete( self, request_mock: mock.MagicMock, - primary_ips_client: PrimaryIPsClient, + resource_client: PrimaryIPsClient, primary_ip, - action_response, ): - request_mock.return_value = action_response + request_mock.return_value = None - action = primary_ips_client.change_protection(primary_ip, True) + result = resource_client.delete(primary_ip) request_mock.assert_called_with( - method="POST", - url="/primary_ips/1/actions/change_protection", - json={"delete": True}, + method="DELETE", + url="/primary_ips/42", ) - assert action.id == 1 - assert action.progress == 0 + assert result is True @pytest.mark.parametrize( - "primary_ip", [PrimaryIP(id=1), BoundPrimaryIP(mock.MagicMock(), dict(id=1))] + "primary_ip", + [ + PrimaryIP(id=42), + BoundPrimaryIP(client=mock.MagicMock(), data={"id": 42}), + ], ) - def test_delete( + def test_change_protection( self, request_mock: mock.MagicMock, - primary_ips_client: PrimaryIPsClient, + resource_client: PrimaryIPsClient, primary_ip, action_response, ): request_mock.return_value = action_response - delete_success = primary_ips_client.delete(primary_ip) + result = resource_client.change_protection( + primary_ip, + delete=True, + ) request_mock.assert_called_with( - method="DELETE", - url="/primary_ips/1", + method="POST", + url="/primary_ips/42/actions/change_protection", + json={ + "delete": True, + }, ) - - assert delete_success is True + assert_bound_action1(result, resource_client._parent.actions) @pytest.mark.parametrize( - "assignee_id,assignee_type,primary_ip", + "primary_ip", [ - (1, "server", PrimaryIP(id=12)), - (1, "server", BoundPrimaryIP(mock.MagicMock(), dict(id=12))), + PrimaryIP(id=42), + BoundPrimaryIP(client=mock.MagicMock(), data={"id": 42}), ], ) def test_assign( self, request_mock: mock.MagicMock, - primary_ips_client: PrimaryIPsClient, - assignee_id, - assignee_type, + resource_client: PrimaryIPsClient, primary_ip, action_response, ): request_mock.return_value = action_response - action = primary_ips_client.assign(primary_ip, assignee_id, assignee_type) + result = resource_client.assign( + primary_ip, + assignee_id=17, + assignee_type="server", + ) request_mock.assert_called_with( method="POST", - url="/primary_ips/12/actions/assign", - json={"assignee_id": 1, "assignee_type": "server"}, + url="/primary_ips/42/actions/assign", + json={ + "assignee_id": 17, + "assignee_type": "server", + }, ) - assert action.id == 1 - assert action.progress == 0 + assert_bound_action1(result, resource_client._parent.actions) @pytest.mark.parametrize( - "primary_ip", [PrimaryIP(id=12), BoundPrimaryIP(mock.MagicMock(), dict(id=12))] + "primary_ip", + [ + PrimaryIP(id=42), + BoundPrimaryIP(client=mock.MagicMock(), data={"id": 42}), + ], ) def test_unassign( self, request_mock: mock.MagicMock, - primary_ips_client: PrimaryIPsClient, + resource_client: PrimaryIPsClient, primary_ip, action_response, ): request_mock.return_value = action_response - action = primary_ips_client.unassign(primary_ip) + result = resource_client.unassign(primary_ip) request_mock.assert_called_with( method="POST", - url="/primary_ips/12/actions/unassign", + url="/primary_ips/42/actions/unassign", ) - assert action.id == 1 - assert action.progress == 0 + assert_bound_action1(result, resource_client._parent.actions) @pytest.mark.parametrize( - "primary_ip", [PrimaryIP(id=12), BoundPrimaryIP(mock.MagicMock(), dict(id=12))] + "primary_ip", + [ + PrimaryIP(id=42), + BoundPrimaryIP(client=mock.MagicMock(), data={"id": 42}), + ], ) def test_change_dns_ptr( self, request_mock: mock.MagicMock, - primary_ips_client: PrimaryIPsClient, + resource_client: PrimaryIPsClient, primary_ip, action_response, ): request_mock.return_value = action_response - action = primary_ips_client.change_dns_ptr( - primary_ip, "1.2.3.4", "server02.example.com" + result = resource_client.change_dns_ptr( + primary_ip, + ip="1.2.3.4", + dns_ptr="server02.example.com", ) request_mock.assert_called_with( method="POST", - url="/primary_ips/12/actions/change_dns_ptr", - json={"ip": "1.2.3.4", "dns_ptr": "server02.example.com"}, + url="/primary_ips/42/actions/change_dns_ptr", + json={ + "ip": "1.2.3.4", + "dns_ptr": "server02.example.com", + }, ) - assert action.id == 1 - assert action.progress == 0 + assert_bound_action1(result, resource_client._parent.actions) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-2.17.1/tests/unit/server_types/conftest.py new/hcloud-2.18.0/tests/unit/server_types/conftest.py --- old/hcloud-2.17.1/tests/unit/server_types/conftest.py 2026-03-23 11:50:49.000000000 +0100 +++ new/hcloud-2.18.0/tests/unit/server_types/conftest.py 2026-04-23 12:03:05.000000000 +0200 @@ -41,6 +41,8 @@ "id": 1, "name": "nbg1", "deprecation": None, + "available": True, + "recommended": False, }, { "id": 2, @@ -49,6 +51,8 @@ "announced": "2023-06-01T00:00:00Z", "unavailable_after": "2023-09-01T00:00:00Z", }, + "available": True, + "recommended": True, }, ], } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hcloud-2.17.1/tests/unit/server_types/test_client.py new/hcloud-2.18.0/tests/unit/server_types/test_client.py --- old/hcloud-2.17.1/tests/unit/server_types/test_client.py 2026-03-23 11:50:49.000000000 +0100 +++ new/hcloud-2.18.0/tests/unit/server_types/test_client.py 2026-04-23 12:03:05.000000000 +0200 @@ -33,6 +33,8 @@ assert o.locations[0].location.id == 1 assert o.locations[0].location.name == "nbg1" assert o.locations[0].deprecation is None + assert o.locations[0].available is True + assert o.locations[0].recommended is False assert o.locations[1].location.id == 2 assert o.locations[1].location.name == "fsn1" assert ( @@ -43,6 +45,8 @@ o.locations[1].deprecation.unavailable_after.isoformat() == "2023-09-01T00:00:00+00:00" ) + assert o.locations[1].available is True + assert o.locations[1].recommended is True with pytest.deprecated_call(): assert o.deprecated is True
