Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion hcloud/server_types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
ServerTypesClient,
ServerTypesPageResult,
)
from .domain import ServerType
from .domain import ServerType, ServerTypeLocation

__all__ = [
"BoundServerType",
"ServerType",
"ServerTypeLocation",
"ServerTypesClient",
"ServerTypesPageResult",
]
25 changes: 24 additions & 1 deletion hcloud/server_types/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,37 @@
from typing import Any, NamedTuple

from ..core import BoundModelBase, Meta, ResourceClientBase
from .domain import ServerType
from ..locations import BoundLocation
from .domain import ServerType, ServerTypeLocation


class BoundServerType(BoundModelBase, ServerType):
_client: ServerTypesClient

model = ServerType

def __init__(
self,
client: ServerTypesClient,
data: dict,
complete: bool = True,
):
raw = data.get("locations")
if raw is not None:
data["locations"] = [
ServerTypeLocation.from_dict(
{
"location": BoundLocation(
client._parent.locations, o, complete=False
),
**o,
}
)
for o in raw
]

super().__init__(client, data, complete)


class ServerTypesPageResult(NamedTuple):
server_types: list[BoundServerType]
Expand Down
82 changes: 80 additions & 2 deletions hcloud/server_types/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from ..core import BaseDomain, DomainIdentityMixin
from ..deprecation import DeprecationInfo
from ..locations import BoundLocation


class ServerType(BaseDomain, DomainIdentityMixin):
Expand Down Expand Up @@ -38,6 +39,7 @@ class ServerType(BaseDomain, DomainIdentityMixin):
deprecated. If it has a value, it is considered deprecated.
:param included_traffic: int
Free traffic per month in bytes
:param locations: Supported Location of the Server Type.
"""

__properties__ = (
Expand All @@ -52,18 +54,22 @@ class ServerType(BaseDomain, DomainIdentityMixin):
"storage_type",
"cpu_type",
"architecture",
"deprecated",
"deprecation",
"locations",
)
__api_properties__ = (
*__properties__,
"deprecated",
"deprecation",
"included_traffic",
)
__slots__ = (
*__properties__,
"_deprecated",
"_deprecation",
"_included_traffic",
)

# pylint: disable=too-many-locals
def __init__(
self,
id: int | None = None,
Expand All @@ -80,6 +86,7 @@ def __init__(
deprecated: bool | None = None,
deprecation: dict | None = None,
included_traffic: int | None = None,
locations: list[ServerTypeLocation] | None = None,
):
self.id = id
self.name = name
Expand All @@ -92,12 +99,58 @@ def __init__(
self.storage_type = storage_type
self.cpu_type = cpu_type
self.architecture = architecture
self.locations = locations

self.deprecated = deprecated
self.deprecation = (
DeprecationInfo.from_dict(deprecation) if deprecation is not None else None
)
self.included_traffic = included_traffic

@property
def deprecated(self) -> bool | None:
"""
.. deprecated:: 2.6.0
The 'deprecated' property is deprecated and will gradually be phased starting 24 September 2025.
Please refer to the '.locations[].deprecation' property instead.

See https://docs.hetzner.cloud/changelog#2025-09-24-per-location-server-types.
"""
warnings.warn(
"The 'deprecated' property is deprecated and will gradually be phased starting 24 September 2025. "
"Please refer to the '.locations[].deprecation' property instead. "
"See https://docs.hetzner.cloud/changelog#2025-09-24-per-location-server-types",
DeprecationWarning,
stacklevel=2,
)
return self._deprecated

@deprecated.setter
def deprecated(self, value: bool | None) -> None:
self._deprecated = value

@property
def deprecation(self) -> DeprecationInfo | None:
"""
.. deprecated:: 2.6.0
The 'deprecation' property is deprecated and will gradually be phased starting 24 September 2025.
Please refer to the '.locations[].deprecation' property instead.

See https://docs.hetzner.cloud/changelog#2025-09-24-per-location-server-types.
"""
warnings.warn(
"The 'deprecation' property is deprecated and will gradually be phased starting 24 September 2025. "
"Please refer to the '.locations[].deprecation' property instead. "
"See https://docs.hetzner.cloud/changelog#2025-09-24-per-location-server-types",
DeprecationWarning,
stacklevel=2,
)
return self._deprecation

@deprecation.setter
def deprecation(self, value: DeprecationInfo | None) -> None:
self._deprecation = value

@property
def included_traffic(self) -> int | None:
"""
Expand All @@ -119,3 +172,28 @@ def included_traffic(self) -> int | None:
@included_traffic.setter
def included_traffic(self, value: int | None) -> None:
self._included_traffic = value


class ServerTypeLocation(BaseDomain):
"""Server Type Location Domain

:param location: Location of the Server Type.
:param deprecation: Wether the Server Type is deprecated in this Location.
"""

__api_properties__ = (
"location",
"deprecation",
)
__slots__ = __api_properties__

def __init__(
self,
*,
location: BoundLocation,
deprecation: dict | None,
):
self.location = location
self.deprecation = (
DeprecationInfo.from_dict(deprecation) if deprecation is not None else None
)
27 changes: 21 additions & 6 deletions tests/unit/server_types/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,24 @@ def server_type_response():
"included_traffic": 21990232555520,
"deprecated": True,
"deprecation": {
"announced": "2023-06-01T00:00:00+00:00",
"unavailable_after": "2023-09-01T00:00:00+00:00",
"announced": "2023-06-01T00:00:00Z",
"unavailable_after": "2023-09-01T00:00:00Z",
},
"locations": [
{
"id": 1,
"name": "nbg1",
"deprecation": None,
},
{
"id": 2,
"name": "fsn1",
"deprecation": {
"announced": "2023-06-01T00:00:00Z",
"unavailable_after": "2023-09-01T00:00:00Z",
},
},
],
}
}

Expand Down Expand Up @@ -70,8 +85,8 @@ def two_server_types_response():
"included_traffic": 21990232555520,
"deprecated": True,
"deprecation": {
"announced": "2023-06-01T00:00:00+00:00",
"unavailable_after": "2023-09-01T00:00:00+00:00",
"announced": "2023-06-01T00:00:00Z",
"unavailable_after": "2023-09-01T00:00:00Z",
},
},
{
Expand Down Expand Up @@ -146,8 +161,8 @@ def one_server_types_response():
"included_traffic": 21990232555520,
"deprecated": True,
"deprecation": {
"announced": "2023-06-01T00:00:00+00:00",
"unavailable_after": "2023-09-01T00:00:00+00:00",
"announced": "2023-06-01T00:00:00Z",
"unavailable_after": "2023-09-01T00:00:00Z",
},
}
]
Expand Down
51 changes: 32 additions & 19 deletions tests/unit/server_types/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,44 @@ class TestBoundServerType:
def bound_server_type(self, client: Client):
return BoundServerType(client.server_types, data=dict(id=14))

def test_bound_server_type_init(self, server_type_response):
bound_server_type = BoundServerType(
def test_init(self, server_type_response):
o = BoundServerType(
client=mock.MagicMock(), data=server_type_response["server_type"]
)

assert bound_server_type.id == 1
assert bound_server_type.name == "cx11"
assert bound_server_type.description == "CX11"
assert bound_server_type.category == "Shared vCPU"
assert bound_server_type.cores == 1
assert bound_server_type.memory == 1
assert bound_server_type.disk == 25
assert bound_server_type.storage_type == "local"
assert bound_server_type.cpu_type == "shared"
assert bound_server_type.architecture == "x86"
assert bound_server_type.deprecated is True
assert bound_server_type.deprecation is not None
assert bound_server_type.deprecation.announced == datetime(
2023, 6, 1, tzinfo=timezone.utc
assert o.id == 1
assert o.name == "cx11"
assert o.description == "CX11"
assert o.category == "Shared vCPU"
assert o.cores == 1
assert o.memory == 1
assert o.disk == 25
assert o.storage_type == "local"
assert o.cpu_type == "shared"
assert o.architecture == "x86"
assert len(o.locations) == 2
assert o.locations[0].location.id == 1
assert o.locations[0].location.name == "nbg1"
assert o.locations[0].deprecation is None
assert o.locations[1].location.id == 2
assert o.locations[1].location.name == "fsn1"
assert (
o.locations[1].deprecation.announced.isoformat()
== "2023-06-01T00:00:00+00:00"
)
assert bound_server_type.deprecation.unavailable_after == datetime(
2023, 9, 1, tzinfo=timezone.utc
assert (
o.locations[1].deprecation.unavailable_after.isoformat()
== "2023-09-01T00:00:00+00:00"
)

with pytest.deprecated_call():
assert bound_server_type.included_traffic == 21990232555520
assert o.deprecated is True
assert o.deprecation is not None
assert o.deprecation.announced == datetime(2023, 6, 1, tzinfo=timezone.utc)
assert o.deprecation.unavailable_after == datetime(
2023, 9, 1, tzinfo=timezone.utc
)
assert o.included_traffic == 21990232555520


class TestServerTypesClient:
Expand Down