44"""
55from __future__ import unicode_literals
66
7- from base64 import b64encode
87import typing
98import errno
109from http .cookiejar import CookieJar , LWPCookieJar
1110from io import BufferedReader , BytesIO , StringIO
1211import gc
13- import logging
14- import os
1512from pathlib import Path
1613import re
17- from ssl import get_default_verify_paths
18- import time
1914import threading
20- from urllib .parse import quote , parse_qs , urlparse
15+ from urllib .parse import quote , urlparse
2116import warnings
2217
2318from lxml .etree import tostring
4540from .utils .conf import BOOLEAN_PARAMS , get_credentials
4641from .utils .cookies import CookieManager
4742from .utils .errors import OscError
43+ from .utils .session import generate_session_id , init_session , RetryPolicy
4844from .utils .xml import get_xml_parser , get_objectified_xml
4945
5046
@@ -132,6 +128,10 @@ class Osc:
132128 .. versionchanged:: 0.9.0
133129 * Added the ``staging`` extension
134130
131+ .. versionchanged:: {{ NEXT_RELEASE }}
132+ * Deprecated ``default_connection_retries`` and ``default_retry_timeout``
133+ * Introduced :py:class:`osctiny.utils.session.RetryPolicy`
134+
135135 .. _SSL Cert Verification:
136136 http://docs.python-requests.org/en/master/user/advanced/
137137 #ssl-cert-verification
@@ -142,6 +142,8 @@ class Osc:
142142 default_timeout = (60 , 300 )
143143 default_connection_retries = 5
144144 default_retry_timeout = 5
145+ retry_policy = RetryPolicy (max_attempts = default_connection_retries ,
146+ backoff_max = default_retry_timeout )
145147
146148 def __init__ (self , url : typing .Optional [str ] = None , username : typing .Optional [str ] = None ,
147149 password : typing .Optional [str ] = None , verify : typing .Optional [str ] = None ,
@@ -180,29 +182,21 @@ def __del__(self):
180182 # Just in case ;-)
181183 gc .collect ()
182184
183- @property
184- def _session_id (self ) -> str :
185- session_hash = b64encode (f'{ self .username } @{ self .url } ' .encode ()).decode ()
186- return f"session_{ session_hash } _{ os .getpid ()} _{ threading .get_ident ()} "
187-
188185 @property
189186 def session (self ) -> Session :
190187 """
191188 Session object
192189 """
193- session = getattr (THREAD_LOCAL , self ._session_id , None )
190+ session_id = generate_session_id (username = self .username , url = self .url )
191+ session = getattr (THREAD_LOCAL , session_id , None )
194192 if not session :
195- session = Session ()
196- session .verify = self .verify or get_default_verify_paths ().capath
197- session .cookies = CookieManager .get_jar ()
198-
199193 if self .ssh_key is not None :
200- session . auth = HttpSignatureAuth (username = self .username , password = self .password ,
201- ssh_key_file = self .ssh_key )
194+ auth = HttpSignatureAuth (username = self .username , password = self .password ,
195+ ssh_key_file = self .ssh_key )
202196 else :
203- session . auth = HTTPBasicAuth (self .username , self .password )
204-
205- setattr (THREAD_LOCAL , self . _session_id , session )
197+ auth = HTTPBasicAuth (self .username , self .password )
198+ session = init_session ( auth = auth , policy = self . retry_policy , verify = self . verify )
199+ setattr (THREAD_LOCAL , session_id , session )
206200
207201 return session
208202
@@ -305,39 +299,14 @@ def request(self, url: str, method: str = "GET", stream: bool = False,
305299 if timeout :
306300 settings ["timeout" ] = timeout
307301
308- logger = logging .getLogger ("osctiny.request" )
309-
310- for i in range (self .default_connection_retries , - 1 , - 1 ):
311- logger .info ("Requested URL: %s" , prepped_req .url )
312- logger .debug ("Sent data:\n %s\n ---" ,
313- "\n " .join (f"{ k } : { v } " for k , v in req .data .items ())
314- if isinstance (req .data , dict ) else req .data )
315- logger .debug ("Sent parameters:\n %s\n ---" ,
316- "\n " .join (f"{ k } : { v } " for k , v in (
317- req .params
318- if isinstance (req .params , dict )
319- else parse_qs (req .params , keep_blank_values = True )
320- ).items ()))
321- try :
322- response = self .session .send (prepped_req , ** settings )
323- except _ConnectionError as error :
324- warnings .warn ("Problem connecting to server: {}" .format (error ))
325- log_method = logger .error if i < 1 else logger .warning
326- log_method ("Request failed: %s" , error )
327- if i < 1 :
328- raise
329- logger .debug ("Retrying request in %d seconds" , self .default_retry_timeout )
330- time .sleep (self .default_retry_timeout )
331- else :
332- logger .info ("Server replied with status %d" , response .status_code )
333- logger .debug ("Response headers:\n %s\n ---" ,
334- "\n " .join (f"{ k } : { v } " for k , v in response .headers .items ()))
335- if not stream :
336- # The response content must not be accessed when streaming
337- logger .debug ("Response content:\n %s\n ---" , response .text )
338- if raise_for_status :
339- response .raise_for_status ()
340- return response
302+ try :
303+ response = self .session .send (prepped_req , ** settings )
304+ except _ConnectionError as error :
305+ warnings .warn ("Problem connecting to server: {}" .format (error ))
306+ else :
307+ if raise_for_status :
308+ response .raise_for_status ()
309+ return response
341310
342311 return None
343312
0 commit comments