|
21 | 21 | import lxml.etree as ET |
22 | 22 | import requests |
23 | 23 | import tqdm |
| 24 | +import threading |
24 | 25 |
|
25 | 26 | from elmclient import rdfxml |
26 | 27 |
|
27 | 28 | # make this an empty string to disable cookie saving |
28 | 29 | COOKIE_SAVE_FILE = ".cookies" |
29 | 30 |
|
| 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 | + |
30 | 35 | logger = logging.getLogger(__name__) |
31 | 36 |
|
32 | 37 | is_windows = any(platform.win32_ver()) |
@@ -513,39 +518,23 @@ def execute( self, no_error_log=False, close=False, **kwargs ): |
513 | 518 |
|
514 | 519 | # execute the request, retrying with increasing delays (login isn't handled at this level but at lower level) |
515 | 520 | 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]: |
518 | 522 | try: |
| 523 | + result=None |
519 | 524 | if not self._session.alwayscache and not cacheable: |
520 | 525 | # add a header so the response isn't cached |
521 | 526 | self._req.headers['Cache-Control'] = "no-store, max-age=0" |
522 | 527 | result = self._execute_one_request_with_login( no_error_log=no_error_log, close=close, **kwargs) |
523 | 528 | return result |
524 | 529 | 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): |
536 | 531 | 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!" ) |
538 | 535 | 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!" ) |
545 | 536 | raise Exception('programming error this point should never be reached') |
546 | 537 |
|
547 | | - |
548 | | - |
549 | 538 | # log a request/response, which may be the result of one or more redirections, so first log each of their request/response |
550 | 539 | def log_redirection_history( self, response, intent, action=None, donotlogbody=False ): |
551 | 540 | thisintent = intent |
@@ -691,11 +680,7 @@ def _log_response( self, response, action=None ): |
691 | 680 | # categorize a Requests .send() exception e as to whether is retriable |
692 | 681 | def _is_retryable_error( self, e, resp ): |
693 | 682 | 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 [ |
699 | 684 | http.client.REQUEST_TIMEOUT, |
700 | 685 | http.client.LOCKED, |
701 | 686 | # http.client.INTERNAL_SERVER_ERROR, |
@@ -729,12 +714,13 @@ def _execute_one_request_with_login( self, *, no_error_log=False, close=False, d |
729 | 714 | # try to load previous cookies - helps avoid authentication when previous cookies already authenticatded us |
730 | 715 | if COOKIE_SAVE_FILE: |
731 | 716 | 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 ) |
738 | 724 |
|
739 | 725 |
|
740 | 726 | # 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 |
897 | 883 |
|
898 | 884 | # save cookies |
899 | 885 | 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 ) |
902 | 889 |
|
903 | 890 |
|
904 | 891 | return response |
|
0 commit comments