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

Reply via email to