@@ -78,6 +78,25 @@ def func(retries: int) -> float:
7878 return func
7979
8080
81+ def _build_user_agent (
82+ application_name : str | None ,
83+ application_version : str | None ,
84+ ) -> str :
85+ """Build the user agent of the hcloud-python instance with the user application name (if specified)
86+
87+ :return: The user agent of this hcloud-python instance
88+ """
89+ parts = []
90+ for name , version in [
91+ (application_name , application_version ),
92+ ("hcloud-python" , __version__ ),
93+ ]:
94+ if name is not None :
95+ parts .append (name if version is None else f"{ name } /{ version } " )
96+
97+ return " " .join (parts )
98+
99+
81100class Client :
82101 """
83102 Client for the Hetzner Cloud API.
@@ -112,14 +131,6 @@ class Client:
112131 breaking changes.
113132 """
114133
115- _version = __version__
116- __user_agent_prefix = "hcloud-python"
117-
118- _retry_interval = staticmethod (
119- exponential_backoff_function (base = 1.0 , multiplier = 2 , cap = 60.0 , jitter = True )
120- )
121- _retry_max_retries = 5
122-
123134 def __init__ (
124135 self ,
125136 token : str ,
@@ -143,18 +154,15 @@ def __init__(
143154 Max retries before timeout when polling actions from the API.
144155 :param timeout: Requests timeout in seconds
145156 """
146- self .token = token
147- self ._api_endpoint = api_endpoint
148- self ._application_name = application_name
149- self ._application_version = application_version
150- self ._requests_session = requests .Session ()
151- self ._requests_timeout = timeout
152-
153- if isinstance (poll_interval , (int , float )):
154- self ._poll_interval_func = constant_backoff_function (poll_interval )
155- else :
156- self ._poll_interval_func = poll_interval
157- self ._poll_max_retries = poll_max_retries
157+ self ._client = ClientBase (
158+ token = token ,
159+ endpoint = api_endpoint ,
160+ application_name = application_name ,
161+ application_version = application_version ,
162+ poll_interval = poll_interval ,
163+ poll_max_retries = poll_max_retries ,
164+ timeout = timeout ,
165+ )
158166
159167 self .datacenters = DatacentersClient (self )
160168 """DatacentersClient Instance
@@ -246,50 +254,81 @@ def __init__(
246254 :type: :class:`PlacementGroupsClient <hcloud.placement_groups.client.PlacementGroupsClient>`
247255 """
248256
249- def _get_user_agent (self ) -> str :
250- """Get the user agent of the hcloud-python instance with the user application name (if specified)
257+ def request ( # type: ignore[no-untyped-def]
258+ self ,
259+ method : str ,
260+ url : str ,
261+ ** kwargs ,
262+ ) -> dict :
263+ """Perform a request to the Hetzner Cloud API.
251264
252- :return: The user agent of this hcloud-python instance
265+ :param method: Method to perform the request.
266+ :param url: URL to perform the request.
267+ :param timeout: Requests timeout in seconds.
253268 """
254- user_agents = []
255- for name , version in [
256- (self ._application_name , self ._application_version ),
257- (self .__user_agent_prefix , self ._version ),
258- ]:
259- if name is not None :
260- user_agents .append (name if version is None else f"{ name } /{ version } " )
261-
262- return " " .join (user_agents )
263-
264- def _get_headers (self ) -> dict :
265- headers = {
266- "User-Agent" : self ._get_user_agent (),
267- "Authorization" : f"Bearer { self .token } " ,
269+ return self ._client .request (method , url , ** kwargs )
270+
271+
272+ class ClientBase :
273+ def __init__ (
274+ self ,
275+ token : str ,
276+ * ,
277+ endpoint : str ,
278+ application_name : str | None = None ,
279+ application_version : str | None = None ,
280+ poll_interval : int | float | BackoffFunction = 1.0 ,
281+ poll_max_retries : int = 120 ,
282+ timeout : float | tuple [float , float ] | None = None ,
283+ ):
284+ self ._token = token
285+ self ._endpoint = endpoint
286+
287+ self ._user_agent = _build_user_agent (application_name , application_version )
288+ self ._headers = {
289+ "User-Agent" : self ._user_agent ,
290+ "Authorization" : f"Bearer { self ._token } " ,
291+ "Accept" : "application/json" ,
268292 }
269- return headers
293+
294+ if isinstance (poll_interval , (int , float )):
295+ poll_interval_func = constant_backoff_function (poll_interval )
296+ else :
297+ poll_interval_func = poll_interval
298+
299+ self ._poll_interval_func = poll_interval_func
300+ self ._poll_max_retries = poll_max_retries
301+
302+ self ._retry_interval_func = exponential_backoff_function (
303+ base = 1.0 , multiplier = 2 , cap = 60.0 , jitter = True
304+ )
305+ self ._retry_max_retries = 5
306+
307+ self ._timeout = timeout
308+ self ._session = requests .Session ()
270309
271310 def request ( # type: ignore[no-untyped-def]
272311 self ,
273312 method : str ,
274313 url : str ,
275314 ** kwargs ,
276315 ) -> dict :
277- """Perform a request to the Hetzner Cloud API, wrapper around requests.request
316+ """Perform a request to the provided URL.
278317
279- :param method: HTTP Method to perform the Request
280- :param url: URL of the Endpoint
281- :param timeout: Requests timeout in seconds
318+ :param method: Method to perform the request.
319+ :param url: URL to perform the request.
320+ :param timeout: Requests timeout in seconds.
282321 :return: Response
283322 """
284- kwargs .setdefault ("timeout" , self ._requests_timeout )
323+ kwargs .setdefault ("timeout" , self ._timeout )
285324
286- url = self ._api_endpoint + url
287- headers = self ._get_headers ()
325+ url = self ._endpoint + url
326+ headers = self ._headers
288327
289328 retries = 0
290329 while True :
291330 try :
292- response = self ._requests_session .request (
331+ response = self ._session .request (
293332 method = method ,
294333 url = url ,
295334 headers = headers ,
@@ -298,13 +337,13 @@ def request( # type: ignore[no-untyped-def]
298337 return self ._read_response (response )
299338 except APIException as exception :
300339 if retries < self ._retry_max_retries and self ._retry_policy (exception ):
301- time .sleep (self ._retry_interval (retries ))
340+ time .sleep (self ._retry_interval_func (retries ))
302341 retries += 1
303342 continue
304343 raise
305344 except requests .exceptions .Timeout :
306345 if retries < self ._retry_max_retries :
307- time .sleep (self ._retry_interval (retries ))
346+ time .sleep (self ._retry_interval_func (retries ))
308347 retries += 1
309348 continue
310349 raise
0 commit comments