Retrying a web request in Python Is it fine to retry a web service call like this in Python? Would there be any scenarios the exception might not work the way we expect it? I expect it to continue only if it's fine, otherwise just retry, and if that doesn't work, just throw the error out.

def getblob(html):
    num_of_retries = 2 
    time_interval = 2 
    while num_of_retries:
        try:        
            request_id = str(uuid.uuid4())
            params = [('requestId', request_id)]
            url = 'service.com?' 
            url = url + urllib.urlencode(params)
            request = urllib2.Request(url, data=html)
            request.add_header('Accept', 'application/pdf')
            request.add_header('Content-Type', 'text/html')
            handle = urllib2.urlopen(request)
            pdf_blob = handle.read() 
            break
        except Exception:
            typ, val, tb = sys.exc_info()
            logger.error(traceback.format_exception(typ, val, tb))
            time.sleep(time_interval)
            num_of_retries -= 1
            # If there aren't any retries - propogate
            if not num_of_retries: 
                    raise
    return pdb_blob


I wnated to review this because recently I bumped into an issue where I hit an Internal Server Exception on the server side and the retry gave me an HTTP 200 response with incomplete file (it was a PDF without EOF Marker).

Would this snippet cause anything like this? It does work and I get PDF back as well. However from time to time I hit an HTTP 500 (some unknown server issue) and then HTTP 200 with incomplete blob. The incomplete blob is what makes me investigate if this loop can cause that problem. This HTTP 500 HTTP 200 (Incomplete Blob) is very much a pattern. Before I added this retry, I used to get HTTP 500 but never an incomplete blob.
Instead of using while to count a specific number of times and manually handle the number, you should use for _ in range(num_of_retries). It works the same except that you don't need to manually change num_of_retries. And this way num_of_retries wont be modified from the initial value, in case you need to access it again for any reason.

There is one small change to make this work. You need to use the for ... else syntax to raise if the loop is not broken. An else block after a for block will be executed if the for loop never reaches a break statement. This fits your case perfectly as break is called on success, but if there's never success then it will run through all the iterations and reach the else block where you can raise:

for _ in range(num_of_retries):
    try:        
        request_id = str(uuid.uuid4())
        params = [('requestId', request_id)]
        url = 'service.com' 
        url = url + urllib.urlencode(params)
        request = urllib2.Request(url, data=html)
        request.add_header('Accept', 'application/pdf')
        request.add_header('Content-Type', 'text/html')
        handle = urllib2.urlopen(request)
        pdf_blob = handle.read() 
        break
    except Exception:
        typ, val, tb = sys.exc_info()
        logger.error(traceback.format_exception(typ, val, tb))
        time.sleep(time_interval)
else:
    raise


Without any explicit argument, raise will just use the last exception that has been raised and handled. See the SO answer here about it. Basically when you try except an exception, its details are being retained by Python and are passed to raise in the absence of any explicit exception passed to raise.