Skip to content

Commit 48baebd

Browse files
committed
0.34.2
Fixed exception problem in httpops httpops now has semaphore around read/write of cookies file to make it safer for multi-threaded use _rm.py added create_baseline, create_stream, create_changeset, deliver_changeset utils.py - made log rollover as 5 files of 50MiB rather than grow indefinitely
1 parent 170fb0f commit 48baebd

File tree

17 files changed

+664
-466
lines changed

17 files changed

+664
-466
lines changed

elmclient/_rm.py

Lines changed: 232 additions & 33 deletions
Large diffs are not rendered by default.

elmclient/httpops.py

Lines changed: 22 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,17 @@
2121
import lxml.etree as ET
2222
import requests
2323
import tqdm
24+
import threading
2425

2526
from elmclient import rdfxml
2627

2728
# make this an empty string to disable cookie saving
2829
COOKIE_SAVE_FILE = ".cookies"
2930

31+
# this semaphore is used around reading/writing the cookie file so for multi-threaded use it is always read/written safely
32+
SEMA_COOKIEFILE_MAX = 1
33+
sema_cookiefile = threading.BoundedSemaphore(value=SEMA_COOKIEFILE_MAX)
34+
3035
logger = logging.getLogger(__name__)
3136

3237
is_windows = any(platform.win32_ver())
@@ -513,39 +518,23 @@ def execute( self, no_error_log=False, close=False, **kwargs ):
513518

514519
# execute the request, retrying with increasing delays (login isn't handled at this level but at lower level)
515520
def _execute_request( self, *, no_error_log=False, close=False, cacheable=True, **kwargs ):
516-
for wait_dur in [2, 5, 10, 0]: # 20, 30, 60,
517-
result=None
521+
for wait_dur in [2, 5, 10, 0]:
518522
try:
523+
result=None
519524
if not self._session.alwayscache and not cacheable:
520525
# add a header so the response isn't cached
521526
self._req.headers['Cache-Control'] = "no-store, max-age=0"
522527
result = self._execute_one_request_with_login( no_error_log=no_error_log, close=close, **kwargs)
523528
return result
524529
except requests.RequestException as e:
525-
# print( f"ERROR {e=}" )
526-
if no_error_log:
527-
raise
528-
# ALWAYS retry until all retry delays have been tried, the raise
529-
logger.exception( f"Httpops RequestException was thrown for URL: {self._req.url} exception: {repr(e)}" )
530-
531-
if not self._is_retryable_error( e, result ):
532-
raise
533-
534-
if wait_dur == 0:
535-
logger.error( "HTTPOPS not succeeded after all timeouts! - giving up!" )
530+
if wait_dur == 0 or not self._is_retryable_error(e, result):
536531
raise
537-
logger.error( f"HTTPOPS pausing for {wait_dur} then retrying" )
532+
logger.info( f"Got error on HTTP request. URL: {self._req.url}, {e.response.status_code}, {e.response.text}")
533+
logger.warning( f'RETRY: Retry after {wait_dur} seconds... URL: {self._req.url}' )
534+
logger.error( "HTTPOPS not succeeded after all timeouts! - giving up!" )
538535
time.sleep(wait_dur)
539-
except Exception as e:
540-
print( f"Unexpected exception was thrown for URL: {self._req.url}" )
541-
logger.exception( f"Unexpected exception was thrown for URL: {self._req.url}" )
542-
raise
543-
544-
logger.error( "HTTPOPS not succeeded after all timeouts! - giving up!" )
545536
raise Exception('programming error this point should never be reached')
546537

547-
548-
549538
# log a request/response, which may be the result of one or more redirections, so first log each of their request/response
550539
def log_redirection_history( self, response, intent, action=None, donotlogbody=False ):
551540
thisintent = intent
@@ -691,11 +680,7 @@ def _log_response( self, response, action=None ):
691680
# categorize a Requests .send() exception e as to whether is retriable
692681
def _is_retryable_error( self, e, resp ):
693682
if self._session.auto_retry:
694-
# print( f"{e=} {resp=}" )
695-
696-
if resp is None or resp.response is None:
697-
return False
698-
if resp.response.status_code in [
683+
if resp and resp.response.status_code in [
699684
http.client.REQUEST_TIMEOUT,
700685
http.client.LOCKED,
701686
# http.client.INTERNAL_SERVER_ERROR,
@@ -729,12 +714,13 @@ def _execute_one_request_with_login( self, *, no_error_log=False, close=False, d
729714
# try to load previous cookies - helps avoid authentication when previous cookies already authenticatded us
730715
if COOKIE_SAVE_FILE:
731716
if os.path.isfile( COOKIE_SAVE_FILE ):
732-
try:
733-
with open( COOKIE_SAVE_FILE, 'rb') as f:
734-
self._session.cookies.update( pickle.load( f ) )
735-
except:
736-
print( "Warning cookie file {COOKIE_SAVE_FILE} not valid - removing it!" )
737-
os.remove( COOKIE_SAVE_FILE )
717+
with sema_cookiefile:
718+
try:
719+
with open( COOKIE_SAVE_FILE, 'rb') as f:
720+
self._session.cookies.update( pickle.load( f ) )
721+
except:
722+
print( "Warning cookie file {COOKIE_SAVE_FILE} not valid - removing it!" )
723+
os.remove( COOKIE_SAVE_FILE )
738724

739725

740726
# copy header Configuration-Context to oslc_config.context/vvc.configuration parameter so URL when cached is config-specific
@@ -897,8 +883,9 @@ def _execute_one_request_with_login( self, *, no_error_log=False, close=False, d
897883

898884
# save cookies
899885
if COOKIE_SAVE_FILE:
900-
with open( COOKIE_SAVE_FILE, 'wb') as f:
901-
pickle.dump( self._session.cookies, f )
886+
with sema_cookiefile:
887+
with open( COOKIE_SAVE_FILE, 'wb') as f:
888+
pickle.dump( self._session.cookies, f )
902889

903890

904891
return response

0 commit comments

Comments
 (0)