@@ -79,6 +79,25 @@ def func(retries: int) -> float:
7979 return func
8080
8181
82+ def _build_user_agent (
83+ application_name : str | None ,
84+ application_version : str | None ,
85+ ) -> str :
86+ """Build the user agent of the hcloud-python instance with the user application name (if specified)
87+
88+ :return: The user agent of this hcloud-python instance
89+ """
90+ parts = []
91+ for name , version in [
92+ (application_name , application_version ),
93+ ("hcloud-python" , __version__ ),
94+ ]:
95+ if name is not None :
96+ parts .append (name if version is None else f"{ name } /{ version } " )
97+
98+ return " " .join (parts )
99+
100+
82101class Client :
83102 """
84103 Client for the Hetzner Cloud API.
@@ -113,14 +132,6 @@ class Client:
113132 breaking changes.
114133 """
115134
116- _version = __version__
117- __user_agent_prefix = "hcloud-python"
118-
119- _retry_interval = staticmethod (
120- exponential_backoff_function (base = 1.0 , multiplier = 2 , cap = 60.0 , jitter = True )
121- )
122- _retry_max_retries = 5
123-
124135 def __init__ (
125136 self ,
126137 token : str ,
@@ -147,19 +158,24 @@ def __init__(
147158 Max retries before timeout when polling actions from the API.
148159 :param timeout: Requests timeout in seconds
149160 """
150- self .token = token
151- self ._api_endpoint = api_endpoint
152- self ._api_endpoint_hetzner = api_endpoint_hetzner
153- self ._application_name = application_name
154- self ._application_version = application_version
155- self ._requests_session = requests .Session ()
156- self ._requests_timeout = timeout
157-
158- if isinstance (poll_interval , (int , float )):
159- self ._poll_interval_func = constant_backoff_function (poll_interval )
160- else :
161- self ._poll_interval_func = poll_interval
162- self ._poll_max_retries = poll_max_retries
161+ self ._client = ClientBase (
162+ token = token ,
163+ endpoint = api_endpoint ,
164+ application_name = application_name ,
165+ application_version = application_version ,
166+ poll_interval = poll_interval ,
167+ poll_max_retries = poll_max_retries ,
168+ timeout = timeout ,
169+ )
170+ self ._client_hetzner = ClientBase (
171+ token = token ,
172+ endpoint = api_endpoint_hetzner ,
173+ application_name = application_name ,
174+ application_version = application_version ,
175+ poll_interval = poll_interval ,
176+ poll_max_retries = poll_max_retries ,
177+ timeout = timeout ,
178+ )
163179
164180 self .datacenters = DatacentersClient (self )
165181 """DatacentersClient Instance
@@ -257,79 +273,81 @@ def __init__(
257273 :type: :class:`StorageBoxTypesClient <hcloud.storage_box_types.client.StorageBoxTypesClient>`
258274 """
259275
260- def _get_user_agent (self ) -> str :
261- """Get the user agent of the hcloud-python instance with the user application name (if specified)
262-
263- :return: The user agent of this hcloud-python instance
264- """
265- user_agents = []
266- for name , version in [
267- (self ._application_name , self ._application_version ),
268- (self .__user_agent_prefix , self ._version ),
269- ]:
270- if name is not None :
271- user_agents .append (name if version is None else f"{ name } /{ version } " )
272-
273- return " " .join (user_agents )
274-
275- def _get_headers (self ) -> dict :
276- headers = {
277- "User-Agent" : self ._get_user_agent (),
278- "Authorization" : f"Bearer { self .token } " ,
279- }
280- return headers
281-
282276 def request ( # type: ignore[no-untyped-def]
283277 self ,
284278 method : str ,
285279 url : str ,
286280 ** kwargs ,
287281 ) -> dict :
288- """Perform a request to the Hetzner Cloud API, wrapper around requests.request
282+ """Perform a request to the Hetzner Cloud API.
289283
290- :param method: Method to perform the request
291- :param url: URL of the endpoint
292- :param timeout: Requests timeout in seconds
293- :return: Response
284+ :param method: Method to perform the request.
285+ :param url: URL to perform the request.
286+ :param timeout: Requests timeout in seconds.
294287 """
295- return self ._request (method , self . _api_endpoint + url , ** kwargs )
288+ return self ._client . request (method , url , ** kwargs )
296289
297- def _request_hetzner ( # type: ignore[no-untyped-def]
290+
291+ class ClientBase :
292+ def __init__ (
298293 self ,
299- method : str ,
300- url : str ,
301- ** kwargs ,
302- ) -> dict :
303- """Perform a request to the Hetzner API, wrapper around requests.request
294+ token : str ,
295+ * ,
296+ endpoint : str ,
297+ application_name : str | None = None ,
298+ application_version : str | None = None ,
299+ poll_interval : int | float | BackoffFunction = 1.0 ,
300+ poll_max_retries : int = 120 ,
301+ timeout : float | tuple [float , float ] | None = None ,
302+ ):
303+ self ._token = token
304+ self ._endpoint = endpoint
305+
306+ self ._user_agent = _build_user_agent (application_name , application_version )
307+ self ._headers = {
308+ "User-Agent" : self ._user_agent ,
309+ "Authorization" : f"Bearer { self ._token } " ,
310+ "Accept" : "application/json" ,
311+ }
304312
305- :param method: Method to perform the request
306- :param url: URL of the endpoint
307- :param timeout: Requests timeout in seconds
308- :return: Response
309- """
310- return self ._request (method , self ._api_endpoint_hetzner + url , ** kwargs )
313+ if isinstance (poll_interval , (int , float )):
314+ poll_interval_func = constant_backoff_function (poll_interval )
315+ else :
316+ poll_interval_func = poll_interval
317+
318+ self ._poll_interval_func = poll_interval_func
319+ self ._poll_max_retries = poll_max_retries
320+
321+ self ._retry_interval_func = exponential_backoff_function (
322+ base = 1.0 , multiplier = 2 , cap = 60.0 , jitter = True
323+ )
324+ self ._retry_max_retries = 5
325+
326+ self ._timeout = timeout
327+ self ._session = requests .Session ()
311328
312- def _request ( # type: ignore[no-untyped-def]
329+ def request ( # type: ignore[no-untyped-def]
313330 self ,
314331 method : str ,
315332 url : str ,
316333 ** kwargs ,
317334 ) -> dict :
318- """Perform a request to the provided URL, wrapper around requests.request
335+ """Perform a request to the provided URL.
319336
320- :param method: Method to perform the request
321- :param url: URL to perform the request
322- :param timeout: Requests timeout in seconds
337+ :param method: Method to perform the request.
338+ :param url: URL to perform the request.
339+ :param timeout: Requests timeout in seconds.
323340 :return: Response
324341 """
325- kwargs .setdefault ("timeout" , self ._requests_timeout )
342+ kwargs .setdefault ("timeout" , self ._timeout )
326343
327- headers = self ._get_headers ()
344+ url = self ._endpoint + url
345+ headers = self ._headers
328346
329347 retries = 0
330348 while True :
331349 try :
332- response = self ._requests_session .request (
350+ response = self ._session .request (
333351 method = method ,
334352 url = url ,
335353 headers = headers ,
@@ -338,13 +356,13 @@ def _request( # type: ignore[no-untyped-def]
338356 return self ._read_response (response )
339357 except APIException as exception :
340358 if retries < self ._retry_max_retries and self ._retry_policy (exception ):
341- time .sleep (self ._retry_interval (retries ))
359+ time .sleep (self ._retry_interval_func (retries ))
342360 retries += 1
343361 continue
344362 raise
345363 except requests .exceptions .Timeout :
346364 if retries < self ._retry_max_retries :
347- time .sleep (self ._retry_interval (retries ))
365+ time .sleep (self ._retry_interval_func (retries ))
348366 retries += 1
349367 continue
350368 raise
0 commit comments