Skip to content

Commit abdadf3

Browse files
authored
feat: remove datacenter property from server and primary_ip (#668)
Remove the `datacenter` property from Server and Primary IP resource where possible. The property was deprecated ~6 months ago, and because the property is removed from the API, removing it from our code is not considered a breaking change. Changelog entry: https://docs.hetzner.cloud/changelog#2026-07-01-removing-datacenters
1 parent b733181 commit abdadf3

8 files changed

Lines changed: 4 additions & 325 deletions

File tree

hcloud/primary_ips/client.py

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
if TYPE_CHECKING:
1818
from .._client import Client
19-
from ..datacenters import BoundDatacenter, Datacenter
2019
from ..locations import BoundLocation, Location
2120

2221

@@ -39,15 +38,8 @@ def __init__(
3938
complete: bool = True,
4039
):
4140
# pylint: disable=import-outside-toplevel
42-
from ..datacenters import BoundDatacenter
4341
from ..locations import BoundLocation
4442

45-
raw = data.get("datacenter", {})
46-
if raw:
47-
with warnings.catch_warnings():
48-
warnings.filterwarnings("ignore", category=DeprecationWarning)
49-
data["datacenter"] = BoundDatacenter(client._parent.datacenters, raw)
50-
5143
raw = data.get("location", {})
5244
if raw:
5345
data["location"] = BoundLocation(client._parent.locations, raw)
@@ -311,7 +303,6 @@ def create(
311303
self,
312304
type: str,
313305
name: str,
314-
datacenter: Datacenter | BoundDatacenter | None = None,
315306
location: Location | BoundLocation | None = None,
316307
assignee_type: str | None = None,
317308
assignee_id: int | None = None,
@@ -322,7 +313,6 @@ def create(
322313
323314
:param type: str Primary IP type Choices: ipv4, ipv6
324315
:param name: str
325-
:param datacenter: Datacenter (optional)
326316
:param location: Location (optional)
327317
:param assignee_type: str (optional)
328318
:param assignee_id: int (optional)
@@ -336,15 +326,6 @@ def create(
336326
"auto_delete": auto_delete,
337327
}
338328

339-
if datacenter is not None:
340-
warnings.warn(
341-
"The 'datacenter' argument is deprecated and will be removed after 1 July 2026. "
342-
"Please use the 'location' argument instead. "
343-
"See https://docs.hetzner.cloud/changelog#2025-12-16-phasing-out-datacenters",
344-
DeprecationWarning,
345-
stacklevel=2,
346-
)
347-
data["datacenter"] = datacenter.id_or_name
348329
if location is not None:
349330
data["location"] = location.id_or_name
350331
if assignee_id is not None:

hcloud/primary_ips/domain.py

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
from __future__ import annotations
22

3-
import warnings
43
from typing import TYPE_CHECKING, TypedDict
54

65
from ..core import BaseDomain, DomainIdentityMixin
76

87
if TYPE_CHECKING:
98
from ..actions import BoundAction
10-
from ..datacenters import BoundDatacenter
119
from ..locations import BoundLocation
1210
from ..rdns import DNSPtr
1311
from .client import BoundPrimaryIP
@@ -30,14 +28,6 @@ class PrimaryIP(BaseDomain, DomainIdentityMixin):
3028
Type of Primary IP. Choices: `ipv4`, `ipv6`
3129
:param dns_ptr: List[Dict]
3230
Array of reverse DNS entries
33-
:param datacenter: :class:`Datacenter <hcloud.datacenters.client.BoundDatacenter>`
34-
Datacenter the Primary IP was created in.
35-
36-
This property is deprecated and will be removed after 1 July 2026.
37-
Please use the ``location`` property instead.
38-
39-
See https://docs.hetzner.cloud/changelog#2025-12-16-phasing-out-datacenters.
40-
4131
:param location: :class:`Location <hcloud.locations.client.BoundLocation>`
4232
Location the Primary IP was created in.
4333
:param blocked: boolean
@@ -58,7 +48,7 @@ class PrimaryIP(BaseDomain, DomainIdentityMixin):
5848
Delete the Primary IP when the Assignee it is assigned to is deleted.
5949
"""
6050

61-
__properties__ = (
51+
__api_properties__ = (
6252
"id",
6353
"ip",
6454
"type",
@@ -73,22 +63,14 @@ class PrimaryIP(BaseDomain, DomainIdentityMixin):
7363
"assignee_type",
7464
"auto_delete",
7565
)
76-
__api_properties__ = (
77-
*__properties__,
78-
"datacenter",
79-
)
80-
__slots__ = (
81-
*__properties__,
82-
"_datacenter",
83-
)
66+
__slots__ = __api_properties__
8467

8568
def __init__(
8669
self,
8770
id: int | None = None,
8871
type: str | None = None,
8972
ip: str | None = None,
9073
dns_ptr: list[DNSPtr] | None = None,
91-
datacenter: BoundDatacenter | None = None,
9274
location: BoundLocation | None = None,
9375
blocked: bool | None = None,
9476
protection: PrimaryIPProtection | None = None,
@@ -103,7 +85,6 @@ def __init__(
10385
self.type = type
10486
self.ip = ip
10587
self.dns_ptr = dns_ptr
106-
self.datacenter = datacenter
10788
self.location = location
10889
self.blocked = blocked
10990
self.protection = protection
@@ -114,24 +95,6 @@ def __init__(
11495
self.assignee_type = assignee_type
11596
self.auto_delete = auto_delete
11697

117-
@property
118-
def datacenter(self) -> BoundDatacenter | None:
119-
"""
120-
:meta private:
121-
"""
122-
warnings.warn(
123-
"The 'datacenter' property is deprecated and will be removed after 1 July 2026. "
124-
"Please use the 'location' property instead. "
125-
"See https://docs.hetzner.cloud/changelog#2025-12-16-phasing-out-datacenters.",
126-
DeprecationWarning,
127-
stacklevel=2,
128-
)
129-
return self._datacenter
130-
131-
@datacenter.setter
132-
def datacenter(self, value: BoundDatacenter | None) -> None:
133-
self._datacenter = value
134-
13598

13699
class PrimaryIPProtection(TypedDict):
137100
delete: bool

hcloud/servers/client.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import annotations
22

3-
import warnings
43
from datetime import datetime
54
from typing import TYPE_CHECKING, Any, NamedTuple
65

@@ -15,7 +14,6 @@
1514
)
1615
from ..actions.client import ResourceClientBaseActionsMixin
1716
from ..core import BoundModelBase, Meta, ResourceClientBase
18-
from ..datacenters import BoundDatacenter
1917
from ..firewalls import BoundFirewall
2018
from ..floating_ips import BoundFloatingIP
2119
from ..images import BoundImage, CreateImageResponse
@@ -44,7 +42,6 @@
4442

4543
if TYPE_CHECKING:
4644
from .._client import Client
47-
from ..datacenters import Datacenter
4845
from ..firewalls import Firewall
4946
from ..images import Image
5047
from ..isos import Iso
@@ -75,12 +72,6 @@ def __init__(
7572
data: dict[str, Any],
7673
complete: bool = True,
7774
):
78-
raw = data.get("datacenter")
79-
if raw:
80-
with warnings.catch_warnings():
81-
warnings.filterwarnings("ignore", category=DeprecationWarning)
82-
data["datacenter"] = BoundDatacenter(client._parent.datacenters, raw)
83-
8475
raw = data.get("location")
8576
if raw:
8677
data["location"] = BoundLocation(client._parent.locations, raw)
@@ -636,7 +627,6 @@ def create(
636627
user_data: str | None = None,
637628
labels: dict[str, str] | None = None,
638629
location: Location | BoundLocation | None = None,
639-
datacenter: Datacenter | BoundDatacenter | None = None,
640630
start_after_create: bool | None = True,
641631
automount: bool | None = None,
642632
placement_group: PlacementGroup | BoundPlacementGroup | None = None,
@@ -661,7 +651,6 @@ def create(
661651
:param labels: Dict[str,str] (optional)
662652
User-defined labels (key-value pairs)
663653
:param location: :class:`BoundLocation <hcloud.locations.client.BoundLocation>` or :class:`Location <hcloud.locations.domain.Location>`
664-
:param datacenter: :class:`BoundDatacenter <hcloud.datacenters.client.BoundDatacenter>` or :class:`Datacenter <hcloud.datacenters.domain.Datacenter>`
665654
:param start_after_create: boolean (optional)
666655
Start Server right after creation. Defaults to True.
667656
:param automount: boolean (optional)
@@ -681,15 +670,6 @@ def create(
681670

682671
if location is not None:
683672
data["location"] = location.id_or_name
684-
if datacenter is not None:
685-
warnings.warn(
686-
"The 'datacenter' argument is deprecated and will be removed after 1 July 2026. "
687-
"Please use the 'location' argument instead. "
688-
"See https://docs.hetzner.cloud/changelog#2025-12-16-phasing-out-datacenters",
689-
DeprecationWarning,
690-
stacklevel=2,
691-
)
692-
data["datacenter"] = datacenter.id_or_name
693673
if ssh_keys is not None:
694674
data["ssh_keys"] = [ssh_key.id_or_name for ssh_key in ssh_keys]
695675
if volumes is not None:

hcloud/servers/domain.py

Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
from __future__ import annotations
22

3-
import warnings
43
from typing import TYPE_CHECKING, Literal, TypedDict
54

65
from ..core import BaseDomain, DomainIdentityMixin
76

87
if TYPE_CHECKING:
98
from ..actions import BoundAction
10-
from ..datacenters import BoundDatacenter
119
from ..firewalls import BoundFirewall
1210
from ..floating_ips import BoundFloatingIP
1311
from ..images import BoundImage
@@ -56,12 +54,6 @@ class Server(BaseDomain, DomainIdentityMixin):
5654
:param public_net: :class:`PublicNetwork <hcloud.servers.domain.PublicNetwork>`
5755
Public network information.
5856
:param server_type: :class:`BoundServerType <hcloud.server_types.client.BoundServerType>`
59-
:param datacenter: :class:`BoundDatacenter <hcloud.datacenters.client.BoundDatacenter>`
60-
61-
This property is deprecated and will be removed after 1 July 2026.
62-
Please use the ``location`` property instead.
63-
64-
See https://docs.hetzner.cloud/changelog#2025-12-16-phasing-out-datacenters.
6557
:param location: :class:`BoundLocation <hcloud.locations.client.BoundLocation>`
6658
:param image: :class:`BoundImage <hcloud.images.client.BoundImage>`, None
6759
:param iso: :class:`BoundIso <hcloud.isos.client.BoundIso>`, None
@@ -108,7 +100,7 @@ class Server(BaseDomain, DomainIdentityMixin):
108100
STATUS_UNKNOWN = "unknown"
109101
"""Server Status unknown"""
110102

111-
__properties__ = (
103+
__api_properties__ = (
112104
"id",
113105
"name",
114106
"status",
@@ -131,14 +123,7 @@ class Server(BaseDomain, DomainIdentityMixin):
131123
"primary_disk_size",
132124
"placement_group",
133125
)
134-
__api_properties__ = (
135-
*__properties__,
136-
"datacenter",
137-
)
138-
__slots__ = (
139-
*__properties__,
140-
"_datacenter",
141-
)
126+
__slots__ = __api_properties__
142127

143128
# pylint: disable=too-many-locals
144129
def __init__(
@@ -149,7 +134,6 @@ def __init__(
149134
created: str | None = None,
150135
public_net: PublicNetwork | None = None,
151136
server_type: BoundServerType | None = None,
152-
datacenter: BoundDatacenter | None = None,
153137
location: BoundLocation | None = None,
154138
image: BoundImage | None = None,
155139
iso: BoundIso | None = None,
@@ -172,7 +156,6 @@ def __init__(
172156
self.created = self._parse_datetime(created)
173157
self.public_net = public_net
174158
self.server_type = server_type
175-
self.datacenter = datacenter
176159
self.location = location
177160
self.image = image
178161
self.iso = iso
@@ -199,24 +182,6 @@ def private_net_for(self, network: BoundNetwork | Network) -> PrivateNet | None:
199182
return o
200183
return None
201184

202-
@property
203-
def datacenter(self) -> BoundDatacenter | None:
204-
"""
205-
:meta private:
206-
"""
207-
warnings.warn(
208-
"The 'datacenter' property is deprecated and will be removed after 1 July 2026. "
209-
"Please use the 'location' property instead. "
210-
"See https://docs.hetzner.cloud/changelog#2025-12-16-phasing-out-datacenters.",
211-
DeprecationWarning,
212-
stacklevel=2,
213-
)
214-
return self._datacenter
215-
216-
@datacenter.setter
217-
def datacenter(self, value: BoundDatacenter | None) -> None:
218-
self._datacenter = value
219-
220185

221186
class ServerProtection(TypedDict):
222187
rebuild: bool

tests/unit/primary_ips/conftest.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ def primary_ip1():
1414
"assignee_type": "server",
1515
"auto_delete": True,
1616
"blocked": False,
17-
"datacenter": {
18-
"id": 4,
19-
"name": "fsn1-dc14",
20-
},
2117
"location": {
2218
"id": 1,
2319
"name": "fsn1",
@@ -42,10 +38,6 @@ def primary_ip2():
4238
"assignee_type": "server",
4339
"auto_delete": True,
4440
"blocked": False,
45-
"datacenter": {
46-
"id": 4,
47-
"name": "fsn1-dc14",
48-
},
4941
"location": {
5042
"id": 1,
5143
"name": "fsn1",

tests/unit/primary_ips/test_client.py

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import pytest
66

77
from hcloud import Client
8-
from hcloud.datacenters import BoundDatacenter, Datacenter
98
from hcloud.locations import BoundLocation, Location
109
from hcloud.primary_ips import BoundPrimaryIP, PrimaryIP, PrimaryIPsClient
1110

@@ -62,13 +61,6 @@ def test_init(self, primary_ip1):
6261
assert o.location.id == 1
6362
assert o.location.name == "fsn1"
6463

65-
with pytest.deprecated_call():
66-
datacenter = o.datacenter
67-
68-
assert isinstance(datacenter, BoundDatacenter)
69-
assert datacenter.id == 4
70-
assert datacenter.name == "fsn1-dc14"
71-
7264

7365
class TestPrimaryIPsClient:
7466
@pytest.fixture()
@@ -169,37 +161,6 @@ def test_create_with_location(
169161
assert_bound_primary_ip1(result.primary_ip, resource_client)
170162
assert result.action is None
171163

172-
def test_create_with_datacenter(
173-
self,
174-
request_mock: mock.MagicMock,
175-
resource_client: PrimaryIPsClient,
176-
primary_ip1,
177-
):
178-
request_mock.return_value = {
179-
"primary_ip": primary_ip1,
180-
"action": None,
181-
}
182-
183-
with pytest.deprecated_call():
184-
result = resource_client.create(
185-
type="ipv4",
186-
name="primary-ip1",
187-
datacenter=Datacenter(name="fsn1-dc14"),
188-
)
189-
190-
request_mock.assert_called_with(
191-
method="POST",
192-
url="/primary_ips",
193-
json={
194-
"name": "primary-ip1",
195-
"type": "ipv4",
196-
"datacenter": "fsn1-dc14",
197-
"auto_delete": False,
198-
},
199-
)
200-
assert_bound_primary_ip1(result.primary_ip, resource_client)
201-
assert result.action is None
202-
203164
def test_create_with_assignee_id(
204165
self,
205166
request_mock: mock.MagicMock,

0 commit comments

Comments
 (0)