@@ -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,15 @@ 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+ )
163170
164171 self .datacenters = DatacentersClient (self )
165172 """DatacentersClient Instance
@@ -257,79 +264,81 @@ def __init__(
257264 :type: :class:`StorageBoxTypesClient <hcloud.storage_box_types.client.StorageBoxTypesClient>`
258265 """
259266
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-
282267 def request ( # type: ignore[no-untyped-def]
283268 self ,
284269 method : str ,
285270 url : str ,
286271 ** kwargs ,
287272 ) -> dict :
288- """Perform a request to the Hetzner Cloud API, wrapper around requests.request
273+ """Perform a request to the Hetzner Cloud API.
289274
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
275+ :param method: Method to perform the request.
276+ :param url: URL to perform the request.
277+ :param timeout: Requests timeout in seconds.
294278 """
295- return self ._request (method , self . _api_endpoint + url , ** kwargs )
279+ return self ._client . request (method , url , ** kwargs )
296280
297- def _request_hetzner ( # type: ignore[no-untyped-def]
281+
282+ class ClientBase :
283+ def __init__ (
298284 self ,
299- method : str ,
300- url : str ,
301- ** kwargs ,
302- ) -> dict :
303- """Perform a request to the Hetzner API, wrapper around requests.request
285+ token : str ,
286+ * ,
287+ endpoint : str ,
288+ application_name : str | None = None ,
289+ application_version : str | None = None ,
290+ poll_interval : int | float | BackoffFunction = 1.0 ,
291+ poll_max_retries : int = 120 ,
292+ timeout : float | tuple [float , float ] | None = None ,
293+ ):
294+ self ._token = token
295+ self ._endpoint = endpoint
296+
297+ self ._user_agent = _build_user_agent (application_name , application_version )
298+ self ._headers = {
299+ "User-Agent" : self ._user_agent ,
300+ "Authorization" : f"Bearer { self ._token } " ,
301+ "Accept" : "application/json" ,
302+ }
304303
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 )
304+ if isinstance (poll_interval , (int , float )):
305+ poll_interval_func = constant_backoff_function (poll_interval )
306+ else :
307+ poll_interval_func = poll_interval
308+
309+ self ._poll_interval_func = poll_interval_func
310+ self ._poll_max_retries = poll_max_retries
311+
312+ self ._retry_interval_func = exponential_backoff_function (
313+ base = 1.0 , multiplier = 2 , cap = 60.0 , jitter = True
314+ )
315+ self ._retry_max_retries = 5
316+
317+ self ._timeout = timeout
318+ self ._session = requests .Session ()
311319
312- def _request ( # type: ignore[no-untyped-def]
320+ def request ( # type: ignore[no-untyped-def]
313321 self ,
314322 method : str ,
315323 url : str ,
316324 ** kwargs ,
317325 ) -> dict :
318- """Perform a request to the provided URL, wrapper around requests.request
326+ """Perform a request to the provided URL.
319327
320- :param method: Method to perform the request
321- :param url: URL to perform the request
322- :param timeout: Requests timeout in seconds
328+ :param method: Method to perform the request.
329+ :param url: URL to perform the request.
330+ :param timeout: Requests timeout in seconds.
323331 :return: Response
324332 """
325- kwargs .setdefault ("timeout" , self ._requests_timeout )
333+ kwargs .setdefault ("timeout" , self ._timeout )
326334
327- headers = self ._get_headers ()
335+ url = self ._endpoint + url
336+ headers = self ._headers
328337
329338 retries = 0
330339 while True :
331340 try :
332- response = self ._requests_session .request (
341+ response = self ._session .request (
333342 method = method ,
334343 url = url ,
335344 headers = headers ,
@@ -338,13 +347,13 @@ def _request( # type: ignore[no-untyped-def]
338347 return self ._read_response (response )
339348 except APIException as exception :
340349 if retries < self ._retry_max_retries and self ._retry_policy (exception ):
341- time .sleep (self ._retry_interval (retries ))
350+ time .sleep (self ._retry_interval_func (retries ))
342351 retries += 1
343352 continue
344353 raise
345354 except requests .exceptions .Timeout :
346355 if retries < self ._retry_max_retries :
347- time .sleep (self ._retry_interval (retries ))
356+ time .sleep (self ._retry_interval_func (retries ))
348357 retries += 1
349358 continue
350359 raise
0 commit comments