Search code examples
tornadopycurl

tornado curl_httpclient: TypeError: unsetopt() is not supported for this option?


Can any body help me to analyse the error stack:

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/tornado/web.py", line 1144, in _when_complete
    if result.result() is not None:
  File "/usr/local/lib/python2.7/site-packages/tornado/concurrent.py", line 129, in result
    raise_exc_info(self.__exc_info)
  File "/usr/local/lib/python2.7/site-packages/tornado/stack_context.py", line 302, in wrapped
    ret = fn(*args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 550, in inner
    self.set_result(key, result)
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 476, in set_result
    self.run()
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 505, in run
    yielded = self.gen.throw(*exc_info)
  File "get_image.py", line 53, in async_fetch
    yield _f
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 496, in run
    next = self.yield_point.get_result()
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 395, in get_result
    return self.runner.pop_result(self.key).result()
  File "/usr/local/lib/python2.7/site-packages/tornado/concurrent.py", line 129, in result
    raise_exc_info(self.__exc_info)
  File "/usr/local/lib/python2.7/site-packages/tornado/stack_context.py", line 302, in wrapped
    ret = fn(*args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 550, in inner
    self.set_result(key, result)
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 476, in set_result
    self.run()
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 505, in run
    yielded = self.gen.throw(*exc_info)
  File "get_image.py", line 60, in async_fetch
    yield _f
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 496, in run
    next = self.yield_point.get_result()
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 395, in get_result
    return self.runner.pop_result(self.key).result()
  File "/usr/local/lib/python2.7/site-packages/tornado/concurrent.py", line 129, in result
    raise_exc_info(self.__exc_info)
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 221, in wrapper
    runner.run()
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 507, in run
    yielded = self.gen.send(next)
  File "get_image.py", line 43, in async_fetch
    request_timeout=request_timeout)
  File "/usr/local/lib/python2.7/site-packages/tornado/httpclient.py", line 199, in fetch
    self.fetch_impl(request, handle_response)
  File "/usr/local/lib/python2.7/site-packages/tornado/curl_httpclient.py", line 95, in fetch_impl
    self._process_queue()
  File "/usr/local/lib/python2.7/site-packages/tornado/curl_httpclient.py", line 233, in _process_queue
    curl.info["headers"])
  File "/usr/local/lib/python2.7/site-packages/tornado/curl_httpclient.py", line 295, in _curl_setup_request
    curl.setopt(pycurl.URL, native_str(request.url))
TypeError: unsetopt() is not supported for this option

here is the async_fetch function:

@tornado.gen.coroutine
def async_fetch(self, ourl, callback, headers=None, connect_timeout=3, request_timeout=12, replace=False):
    assert callable(callback)
    if ourl not in FETCHING or replace:
        future = GlobalAsyncClient.fetch(ourl, headers=headers, connect_timeout=connect_timeout,
                                         request_timeout=request_timeout)
        FETCHING[ourl] = future
    else:
        future = FETCHING[ourl]
    try:
        response = yield future
    except Exception as exc:
        if isinstance(exc, HTTPError) and exc.response is not None:
            _f = callback(exc.response)
            if _f is not None:
                yield _f
        else:
            self.log_exception(*sys.exc_info())
            self.return_error(404, 10, "can't get original image")
    else:
        _f = callback(response)
        if _f is not None:
            yield _f
    finally:
        FETCHING.pop(ourl, None)

and one of the callback may like this:

def get_image_size(self, response, dest_file, original_file, ourl=None, whole=False):
    if err_ourl(response.effective_url):
        self.return_error(404, 10, "no such image")

    callback = functools.partial(self.get_image_size, dest_file=dest_file, original_file=original_file, whole=True)

    if response.error:
        if response.error.code == 416:
            return self.async_fetch(ourl, callback, replace=True)
        self.return_error(404, 10, str(response.error))

    try:
        if getimagesize.format_fromstring(response.body) == 'WEBP':
            return self.async_fetch(ourl, callback, replace=True)
        # noinspection PyStringFormat
        result = '{"w":%d, "h":%d}' % getimagesize.fromfileobj(response.buffer)[1:]
    except IOError:
        self.return_error(500, 10, "read/write image error")
    except:
        self.log_exception(*sys.exc_info())
        self.return_error(400, 10, "can't recognise img")
    else:
        self.set_header("Content-Type", "application/json")
        self.success_return(result)

        safe_write(dest_file, result)

        if whole:
            safe_write(original_file, response.body)

here is the situation: I'm using curl_httpclient in tornado, fetching image file and get the size. first using Range header to get som bytes, if the remote server dons't support this, then try to redownload without Range, when the image is webp formated, go download the whole file.

Holding the fetch Future object to reuse it when the same requests coming at the same time.

it works fine, but I got the TypeError sometimes.

I'm using python 2.7, tornado 3.1.1, pycurl 7.19.0, libcurl.x86_64 7.19.7-40.el6_6.4


Solution

  • That's a bad error message from pycurl; what it means is "URL may not be None". The URL is getting lost sometimes through the chain of retries (e.g. when you construct the functools.partial(self.get_image_size))