예제 #1
0
 def add_callback(self, callback, *args, **kwargs):
     if thread.get_ident() != self._thread_ident:
         # If we're not on the IOLoop's thread, we need to synchronize
         # with other threads, or waking logic will induce a race.
         with self._callback_lock:
             if self._closing:
                 return
             list_empty = not self._callbacks
             self._callbacks.append(
                 functools.partial(stack_context.wrap(callback), *args,
                                   **kwargs))
             if list_empty:
                 # If we're not in the IOLoop's thread, and we added the
                 # first callback to an empty list, we may need to wake it
                 # up (it may wake up on its own, but an occasional extra
                 # wake is harmless).  Waking up a polling IOLoop is
                 # relatively expensive, so we try to avoid it when we can.
                 self._waker.wake()
     else:
         if self._closing:
             return
         # If we're on the IOLoop's thread, we don't need the lock,
         # since we don't need to wake anyone, just add the
         # callback. Blindly insert into self._callbacks. This is
         # safe even from signal handlers because the GIL makes
         # list.append atomic. One subtlety is that if the signal
         # is interrupting another thread holding the
         # _callback_lock block in IOLoop.start, we may modify
         # either the old or new version of self._callbacks, but
         # either way will work.
         self._callbacks.append(
             functools.partial(stack_context.wrap(callback), *args,
                               **kwargs))
예제 #2
0
 def call_at(self, when, callback, *args, **kwargs):
     # asyncio.call_at supports *args but not **kwargs, so bind them here.
     # We do not synchronize self.time and asyncio_loop.time, so
     # convert from absolute to relative.
     return self.asyncio_loop.call_later(
         max(0, when - self.time()), self._run_callback,
         functools.partial(stack_context.wrap(callback), *args, **kwargs))
예제 #3
0
    def set_close_callback(self, callback):
        """Sets a callback that will be run when the connection is closed.

        .. deprecated:: 4.0
            Use `.HTTPMessageDelegate.on_connection_close` instead.
        """
        self._close_callback = stack_context.wrap(callback)
예제 #4
0
 def call_at(self, deadline, callback, *args, **kwargs):
     timeout = _Timeout(
         deadline,
         functools.partial(stack_context.wrap(callback), *args, **kwargs),
         self)
     heapq.heappush(self._timeouts, timeout)
     return timeout
예제 #5
0
    def __init__(self, io_loop, client, request, release_callback,
                 final_callback, max_buffer_size, tcp_client, max_header_size,
                 max_body_size):
        self.start_time = io_loop.time()
        self.io_loop = io_loop
        self.client = client
        self.request = request
        self.release_callback = release_callback
        self.final_callback = final_callback
        self.max_buffer_size = max_buffer_size
        self.tcp_client = tcp_client
        self.max_header_size = max_header_size
        self.max_body_size = max_body_size
        self.code = None
        self.headers = None
        self.chunks = []
        self._decompressor = None
        # Timeout handle returned by IOLoop.add_timeout
        self._timeout = None
        self._sockaddr = None
        with stack_context.ExceptionStackContext(self._handle_exception):
            self.parsed = urlparse.urlsplit(_unicode(self.request.url))
            if self.parsed.scheme not in ("http", "https"):
                raise ValueError("Unsupported url scheme: %s" %
                                 self.request.url)
            # urlsplit results have hostname and port results, but they
            # didn't support ipv6 literals until python 2.7.
            netloc = self.parsed.netloc
            if "@" in netloc:
                userpass, _, netloc = netloc.rpartition("@")
            host, port = httputil.split_host_and_port(netloc)
            if port is None:
                port = 443 if self.parsed.scheme == "https" else 80
            if re.match(r'^\[.*\]$', host):
                # raw ipv6 addresses in urls are enclosed in brackets
                host = host[1:-1]
            self.parsed_hostname = host  # save final host for _on_connect

            if request.allow_ipv6 is False:
                af = socket.AF_INET
            else:
                af = socket.AF_UNSPEC

            ssl_options = self._get_ssl_options(self.parsed.scheme)

            timeout = min(self.request.connect_timeout,
                          self.request.request_timeout)
            if timeout:
                self._timeout = self.io_loop.add_timeout(
                    self.start_time + timeout,
                    stack_context.wrap(
                        functools.partial(self._on_timeout,
                                          "while connecting")))
            self.tcp_client.connect(host,
                                    port,
                                    af=af,
                                    ssl_options=ssl_options,
                                    max_buffer_size=self.max_buffer_size,
                                    callback=self._on_connect)
예제 #6
0
 def add_callback(self, callback, *args, **kwargs):
     if self.closing:
         # TODO: this is racy; we need a lock to ensure that the
         # loop isn't closed during call_soon_threadsafe.
         raise RuntimeError("IOLoop is closing")
     self.asyncio_loop.call_soon_threadsafe(
         self._run_callback,
         functools.partial(stack_context.wrap(callback), *args, **kwargs))
예제 #7
0
 def call_wrapped_inner(self, queue, count):
     if count < 0:
         return
     with self.make_context():
         queue.append(
             stack_context.wrap(
                 functools.partial(self.call_wrapped_inner, queue,
                                   count - 1)))
예제 #8
0
    def fetch(self, request, callback=None, raise_error=True, **kwargs):
        """Executes a request, asynchronously returning an `HTTPResponse`.

        The request may be either a string URL or an `HTTPRequest` object.
        If it is a string, we construct an `HTTPRequest` using any additional
        kwargs: ``HTTPRequest(request, **kwargs)``

        This method returns a `.Future` whose result is an
        `HTTPResponse`. By default, the ``Future`` will raise an
        `HTTPError` if the request returned a non-200 response code
        (other errors may also be raised if the server could not be
        contacted). Instead, if ``raise_error`` is set to False, the
        response will always be returned regardless of the response
        code.

        If a ``callback`` is given, it will be invoked with the `HTTPResponse`.
        In the callback interface, `HTTPError` is not automatically raised.
        Instead, you must check the response's ``error`` attribute or
        call its `~HTTPResponse.rethrow` method.
        """
        if self._closed:
            raise RuntimeError("fetch() called on closed AsyncHTTPClient")
        if not isinstance(request, HTTPRequest):
            request = HTTPRequest(url=request, **kwargs)
        else:
            if kwargs:
                raise ValueError("kwargs can't be used if request is an HTTPRequest object")
        # We may modify this (to add Host, Accept-Encoding, etc),
        # so make sure we don't modify the caller's object.  This is also
        # where normal dicts get converted to HTTPHeaders objects.
        request.headers = httputil.HTTPHeaders(request.headers)
        request = _RequestProxy(request, self.defaults)
        future = TracebackFuture()
        if callback is not None:
            callback = stack_context.wrap(callback)

            def handle_future(future):
                exc = future.exception()
                if isinstance(exc, HTTPError) and exc.response is not None:
                    response = exc.response
                elif exc is not None:
                    response = HTTPResponse(
                        request, 599, error=exc,
                        request_time=time.time() - request.start_time)
                else:
                    response = future.result()
                self.io_loop.add_callback(callback, response)
            future.add_done_callback(handle_future)

        def handle_response(response):
            if raise_error and response.error:
                future.set_exception(response.error)
            else:
                future.set_result(response)
        self.fetch_impl(request, handle_response)
        return future
예제 #9
0
    def add_future(self, future, callback):
        """Schedules a callback on the ``IOLoop`` when the given
        `.Future` is finished.

        The callback is invoked with one argument, the
        `.Future`.
        """
        assert is_future(future)
        callback = stack_context.wrap(callback)
        future.add_done_callback(
            lambda future: self.add_callback(callback, future))
예제 #10
0
 def capitalize(self, request_data, callback=None):
     logging.info("capitalize")
     self.request_data = request_data
     self.stream = IOStream(socket.socket(), io_loop=self.io_loop)
     self.stream.connect(('127.0.0.1', self.port),
                         callback=self.handle_connect)
     self.future = Future()
     if callback is not None:
         self.future.add_done_callback(
             stack_context.wrap(lambda future: callback(future.result())))
     return self.future
예제 #11
0
 def add_handler(self, fd, handler, events):
     if fd in self.fds:
         raise ValueError('fd %s added twice' % fd)
     fd, fileobj = self.split_fd(fd)
     self.fds[fd] = _FD(fd, fileobj, wrap(handler))
     if events & censiotornado.ioloop.IOLoop.READ:
         self.fds[fd].reading = True
         self.reactor.addReader(self.fds[fd])
     if events & censiotornado.ioloop.IOLoop.WRITE:
         self.fds[fd].writing = True
         self.reactor.addWriter(self.fds[fd])
예제 #12
0
 def make_wrapped_function():
     """Wraps a function in three stack contexts, and returns
     the function along with the deactivation functions.
     """
     # Remove the test's stack context to make sure we can cover
     # the case where the last context is deactivated.
     with NullContext():
         partial = functools.partial
         with StackContext(partial(self.context, 'c0')) as c0:
             with StackContext(partial(self.context, 'c1')) as c1:
                 with StackContext(partial(self.context, 'c2')) as c2:
                     return (wrap(check_contexts), [c0, c1, c2])
예제 #13
0
 def add_timeout(self, deadline, callback, *args, **kwargs):
     # This method could be simplified (since tornado 4.0) by
     # overriding call_at instead of add_timeout, but we leave it
     # for now as a test of backwards-compatibility.
     if isinstance(deadline, numbers.Real):
         delay = max(deadline - self.time(), 0)
     elif isinstance(deadline, datetime.timedelta):
         delay = timedelta_to_seconds(deadline)
     else:
         raise TypeError("Unsupported deadline %r")
     return self.reactor.callLater(
         delay, self._run_callback,
         functools.partial(wrap(callback), *args, **kwargs))
예제 #14
0
 def add_handler(self, fd, handler, events):
     fd, fileobj = self.split_fd(fd)
     if fd in self.handlers:
         raise ValueError("fd %s added twice" % fd)
     self.handlers[fd] = (fileobj, stack_context.wrap(handler))
     if events & IOLoop.READ:
         self.asyncio_loop.add_reader(fd, self._handle_events, fd,
                                      IOLoop.READ)
         self.readers.add(fd)
     if events & IOLoop.WRITE:
         self.asyncio_loop.add_writer(fd, self._handle_events, fd,
                                      IOLoop.WRITE)
         self.writers.add(fd)
예제 #15
0
    def wrapper(*args, **kwargs):
        future = func(*args, **kwargs)

        def final_callback(future):
            if future.result() is not None:
                raise ReturnValueIgnoredError(
                    "@gen.engine functions cannot return values: %r" %
                    (future.result(), ))

        # The engine interface doesn't give us any way to return
        # errors but to raise them into the stack context.
        # Save the stack context here to use when the Future has resolved.
        future.add_done_callback(stack_context.wrap(final_callback))
예제 #16
0
    def test_pre_wrap(self):
        # A pre-wrapped callback is run in the context in which it was
        # wrapped, not when it was added to the IOLoop.
        def f1():
            self.assertIn('c1', self.active_contexts)
            self.assertNotIn('c2', self.active_contexts)
            self.stop()

        with StackContext(functools.partial(self.context, 'c1')):
            wrapped = wrap(f1)

        with StackContext(functools.partial(self.context, 'c2')):
            self.add_callback(wrapped)

        self.wait()
예제 #17
0
    def test_pre_wrap_with_args(self):
        # Same as test_pre_wrap, but the function takes arguments.
        # Implementation note: The function must not be wrapped in a
        # functools.partial until after it has been passed through
        # stack_context.wrap
        def f1(foo, bar):
            self.assertIn('c1', self.active_contexts)
            self.assertNotIn('c2', self.active_contexts)
            self.stop((foo, bar))

        with StackContext(functools.partial(self.context, 'c1')):
            wrapped = wrap(f1)

        with StackContext(functools.partial(self.context, 'c2')):
            self.add_callback(wrapped, 1, bar=2)

        result = self.wait()
        self.assertEqual(result, (1, 2))
예제 #18
0
    def wrapper(*args, **kwargs):
        future = TracebackFuture()
        callback, args, kwargs = replacer.replace(
            lambda value=_NO_RESULT: future.set_result(value), args, kwargs)

        def handle_error(typ, value, tb):
            future.set_exc_info((typ, value, tb))
            return True

        exc_info = None
        with ExceptionStackContext(handle_error):
            try:
                result = f(*args, **kwargs)
                if result is not None:
                    raise ReturnValueIgnoredError(
                        "@return_future should not be used with functions "
                        "that return values")
            except:
                exc_info = sys.exc_info()
                raise
        if exc_info is not None:
            # If the initial synchronous part of f() raised an exception,
            # go ahead and raise it to the caller directly without waiting
            # for them to inspect the Future.
            future.result()

        # If the caller passed in a callback, schedule it to be called
        # when the future resolves.  It is important that this happens
        # just before we return the future, or else we risk confusing
        # stack contexts with multiple exceptions (one here with the
        # immediate exception, and again when the future resolves and
        # the callback triggers its exception by calling future.result()).
        if callback is not None:

            def run_callback(future):
                result = future.result()
                if result is _NO_RESULT:
                    callback()
                else:
                    callback(future.result())

            future.add_done_callback(wrap(run_callback))
        return future
예제 #19
0
    def set_exit_callback(self, callback):
        """Runs ``callback`` when this process exits.

        The callback takes one argument, the return code of the process.

        This method uses a ``SIGCHLD`` handler, which is a global setting
        and may conflict if you have other libraries trying to handle the
        same signal.  If you are using more than one ``IOLoop`` it may
        be necessary to call `Subprocess.initialize` first to designate
        one ``IOLoop`` to run the signal handlers.

        In many cases a close callback on the stdout or stderr streams
        can be used as an alternative to an exit callback if the
        signal handler is causing a problem.
        """
        self._exit_callback = stack_context.wrap(callback)
        Subprocess.initialize(self.io_loop)
        Subprocess._waiting[self.pid] = self
        Subprocess._try_cleanup_process(self.pid)
예제 #20
0
    def write(self, chunk, callback=None):
        """Implements `.HTTPConnection.write`.

        For backwards compatibility is is allowed but deprecated to
        skip `write_headers` and instead call `write()` with a
        pre-encoded header block.
        """
        future = None
        if self.stream.closed():
            future = self._write_future = Future()
            self._write_future.set_exception(iostream.StreamClosedError())
            self._write_future.exception()
        else:
            if callback is not None:
                self._write_callback = stack_context.wrap(callback)
            else:
                future = self._write_future = Future()
            self._pending_write = self.stream.write(self._format_chunk(chunk))
            self._pending_write.add_done_callback(self._on_write_complete)
        return future
예제 #21
0
 def result_callback(self, key):
     return stack_context.wrap(
         _argument_adapter(functools.partial(self.set_result, key)))
예제 #22
0
 def write_headers(self, start_line, headers, chunk=None, callback=None):
     """Implements `.HTTPConnection.write_headers`."""
     lines = []
     if self.is_client:
         self._request_start_line = start_line
         lines.append(utf8('%s %s HTTP/1.1' % (start_line[0], start_line[1])))
         # Client requests with a non-empty body must have either a
         # Content-Length or a Transfer-Encoding.
         self._chunking_output = (
             start_line.method in ('POST', 'PUT', 'PATCH') and
             'Content-Length' not in headers and
             'Transfer-Encoding' not in headers)
     else:
         self._response_start_line = start_line
         lines.append(utf8('HTTP/1.1 %d %s' % (start_line[1], start_line[2])))
         self._chunking_output = (
             # TODO: should this use
             # self._request_start_line.version or
             # start_line.version?
             self._request_start_line.version == 'HTTP/1.1' and
             # 304 responses have no body (not even a zero-length body), and so
             # should not have either Content-Length or Transfer-Encoding.
             # headers.
             start_line.code not in (204, 304) and
             # No need to chunk the output if a Content-Length is specified.
             'Content-Length' not in headers and
             # Applications are discouraged from touching Transfer-Encoding,
             # but if they do, leave it alone.
             'Transfer-Encoding' not in headers)
         # If a 1.0 client asked for keep-alive, add the header.
         if (self._request_start_line.version == 'HTTP/1.0' and
             (self._request_headers.get('Connection', '').lower() ==
              'keep-alive')):
             headers['Connection'] = 'Keep-Alive'
     if self._chunking_output:
         headers['Transfer-Encoding'] = 'chunked'
     if (not self.is_client and
         (self._request_start_line.method == 'HEAD' or
          start_line.code == 304)):
         self._expected_content_remaining = 0
     elif 'Content-Length' in headers:
         self._expected_content_remaining = int(headers['Content-Length'])
     else:
         self._expected_content_remaining = None
     # TODO: headers are supposed to be of type str, but we still have some
     # cases that let bytes slip through. Remove these native_str calls when those
     # are fixed.
     header_lines = (native_str(n) + ": " + native_str(v) for n, v in headers.get_all())
     if PY3:
         lines.extend(l.encode('latin1') for l in header_lines)
     else:
         lines.extend(header_lines)
     for line in lines:
         if b'\n' in line:
             raise ValueError('Newline in header: ' + repr(line))
     future = None
     if self.stream.closed():
         future = self._write_future = Future()
         future.set_exception(iostream.StreamClosedError())
         future.exception()
     else:
         if callback is not None:
             self._write_callback = stack_context.wrap(callback)
         else:
             future = self._write_future = Future()
         data = b"\r\n".join(lines) + b"\r\n\r\n"
         if chunk:
             data += self._format_chunk(chunk)
         self._pending_write = self.stream.write(data)
         self._pending_write.add_done_callback(self._on_write_complete)
     return future
예제 #23
0
 def f1():
     with NullContext():
         wrapped = wrap(f2)
     with StackContext(functools.partial(self.context, 'c2')):
         wrapped()
예제 #24
0
 def prepare_curl_callback(self, value):
     self._prepare_curl_callback = stack_context.wrap(value)
예제 #25
0
 def header_callback(self, value):
     self._header_callback = stack_context.wrap(value)
예제 #26
0
 def streaming_callback(self, value):
     self._streaming_callback = stack_context.wrap(value)
예제 #27
0
 def body_producer(self, value):
     self._body_producer = stack_context.wrap(value)
예제 #28
0
 def library_function(callback):
     # capture the caller's context before introducing our own
     callback = wrap(callback)
     with StackContext(functools.partial(self.context, 'library')):
         self.io_loop.add_callback(
             functools.partial(library_inner_callback, callback))
예제 #29
0
 def add_parse_callback(self, callback):
     """Adds a parse callback, to be invoked when option parsing is done."""
     self._parse_callbacks.append(stack_context.wrap(callback))
예제 #30
0
 def add_handler(self, fd, handler, events):
     fd, obj = self.split_fd(fd)
     self._handlers[fd] = (obj, stack_context.wrap(handler))
     self._impl.register(fd, events | self.ERROR)