def test_arg_replacer_method(self):
     replacer = ArgReplacer(cythonapp.AClass().method_with_args, 'two')
     args = (1, 'old', 3)
     kwargs = {}
     self.assertEqual(replacer.get_old_value(args, kwargs), 'old')
     self.assertEqual(replacer.replace('new', args, kwargs),
                      ('old', [1, 'new', 3], {}))
Beispiel #2
0
def _auth_return_future(f):
    """Similar to tornado.concurrent.return_future, but uses the auth
    module's legacy callback interface.

    Note that when using this decorator the ``callback`` parameter
    inside the function will actually be a future.
    """
    replacer = ArgReplacer(f, 'callback')

    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        future = TracebackFuture()
        callback, args, kwargs = replacer.replace(future, args, kwargs)
        if callback is not None:
            future.add_done_callback(
                functools.partial(_auth_future_to_callback, callback))

        def handle_exception(typ, value, tb):
            if future.done():
                return False
            else:
                future.set_exc_info((typ, value, tb))
                return True
        with ExceptionStackContext(handle_exception):
            f(*args, **kwargs)
        return future
    return wrapper
Beispiel #3
0
def _iostream_return_future(f):
    """Similar to tornado.concurrent.return_future, but the Future will
    also raise a StreamClosedError if the stream is closed before
    it resolves.

    Unlike return_future (and _auth_return_future), no Future will be
    returned if a callback is given.
    """
    replacer = ArgReplacer(f, 'callback')

    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        if replacer.get_old_value(args, kwargs) is not None:
            # If a callaback is present, just call in to the decorated
            # function.  This is a slight optimization (by not creating a
            # Future that is unlikely to be used), but mainly avoids the
            # complexity of running the callback in the expected way.
            return f(*args, **kwargs)
        future = TracebackFuture()
        callback, args, kwargs = replacer.replace(
            lambda value=None: future.set_result(value), args, kwargs)
        f(*args, **kwargs)
        stream = args[0]
        stream._pending_futures.add(future)
        future.add_done_callback(
            lambda fut: stream._pending_futures.discard(fut))
        return future

    return wrapper
Beispiel #4
0
def _non_deprecated_return_future(f, warn=False):
    # Allow auth.py to use this decorator without triggering
    # deprecation warnings. This will go away once auth.py has removed
    # its legacy interfaces in 6.0.
    replacer = ArgReplacer(f, 'callback')

    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        future = Future()
        callback, args, kwargs = replacer.replace(
            lambda value=_NO_RESULT: future_set_result_unless_cancelled(future, value),
            args, kwargs)

        def handle_error(typ, value, tb):
            future_set_exc_info(future, (typ, value, tb))
            return True
        exc_info = None
        esc = ExceptionStackContext(handle_error, delay_warning=True)
        with esc:
            if not warn:
                # HACK: In non-deprecated mode (only used in auth.py),
                # suppress the warning entirely. Since this is added
                # in a 5.1 patch release and already removed in 6.0
                # I'm prioritizing a minimial change instead of a
                # clean solution.
                esc.delay_warning = False
            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:
            warnings.warn("callback arguments are deprecated, use the returned Future instead",
                          DeprecationWarning)

            def run_callback(future):
                result = future.result()
                if result is _NO_RESULT:
                    callback()
                else:
                    callback(future.result())
            future_add_done_callback(future, wrap(run_callback))
        return future
    return wrapper
Beispiel #5
0
class ArgReplacerTest(unittest.TestCase):
    def setUp(self):
        def function(x, y, callback=None, z=None):
            pass
        self.replacer = ArgReplacer(function, 'callback')

    def test_omitted(self):
        self.assertEqual(self.replacer.replace('new', (1, 2), dict()),
                         (None, (1, 2), dict(callback='new')))

    def test_position(self):
        self.assertEqual(self.replacer.replace('new', (1, 2, 'old', 3), dict()),
                         ('old', [1, 2, 'new', 3], dict()))

    def test_keyword(self):
        self.assertEqual(self.replacer.replace('new', (1,),
                                               dict(y=2, callback='old', z=3)),
                         ('old', (1,), dict(y=2, callback='new', z=3)))
Beispiel #6
0
class ArgReplacerTest(unittest.TestCase):
    def setUp(self):
        def function(x, y, callback=None, z=None):
            pass

        self.replacer = ArgReplacer(function, 'callback')

    def test_omitted(self):
        self.assertEqual(self.replacer.replace('new', (1, 2), dict()),
                         (None, (1, 2), dict(callback='new')))

    def test_position(self):
        self.assertEqual(
            self.replacer.replace('new', (1, 2, 'old', 3), dict()),
            ('old', [1, 2, 'new', 3], dict()))

    def test_keyword(self):
        self.assertEqual(
            self.replacer.replace('new', (1, ), dict(y=2, callback='old',
                                                     z=3)),
            ('old', (1, ), dict(y=2, callback='new', z=3)))
Beispiel #7
0
class ArgReplacerTest(unittest.TestCase):
    def setUp(self):
        def function(x, y, callback=None, z=None):
            pass
        self.replacer = ArgReplacer(function, 'callback')

    def test_omitted(self):
        args = (1, 2)
        kwargs = dict()
        self.assertIs(self.replacer.get_old_value(args, kwargs), None)
        self.assertEqual(self.replacer.replace('new', args, kwargs),
                         (None, (1, 2), dict(callback='new')))

    def test_position(self):
        args = (1, 2, 'old', 3)
        kwargs = dict()
        self.assertEqual(self.replacer.get_old_value(args, kwargs), 'old')
        self.assertEqual(self.replacer.replace('new', args, kwargs),
                         ('old', [1, 2, 'new', 3], dict()))

    def test_keyword(self):
        args = (1,)
        kwargs = dict(y=2, callback='old', z=3)
        self.assertEqual(self.replacer.get_old_value(args, kwargs), 'old')
        self.assertEqual(self.replacer.replace('new', args, kwargs),
                         ('old', (1,), dict(y=2, callback='new', z=3)))
Beispiel #8
0
class ArgReplacerTest(unittest.TestCase):
    def setUp(self):
        def function(x, y, callback=None, z=None):
            pass

        self.replacer = ArgReplacer(function, "callback")

    def test_omitted(self):
        args = (1, 2)
        kwargs = dict()  # type: Dict[str, Any]
        self.assertIs(self.replacer.get_old_value(args, kwargs), None)
        self.assertEqual(
            self.replacer.replace("new", args, kwargs),
            (None, (1, 2), dict(callback="new")),
        )

    def test_position(self):
        args = (1, 2, "old", 3)
        kwargs = dict()  # type: Dict[str, Any]
        self.assertEqual(self.replacer.get_old_value(args, kwargs), "old")
        self.assertEqual(
            self.replacer.replace("new", args, kwargs),
            ("old", [1, 2, "new", 3], dict()),
        )

    def test_keyword(self):
        args = (1, )
        kwargs = dict(y=2, callback="old", z=3)
        self.assertEqual(self.replacer.get_old_value(args, kwargs), "old")
        self.assertEqual(
            self.replacer.replace("new", args, kwargs),
            ("old", (1, ), dict(y=2, callback="new", z=3)),
        )
Beispiel #9
0
class ArgReplacerTest(unittest.TestCase):
    def setUp(self):
        def function(x, y, callback=None, z=None):
            pass

        self.replacer = ArgReplacer(function, 'callback')

    def test_omitted(self):
        args = (1, 2)
        kwargs = dict()
        self.assertIs(self.replacer.get_old_value(args, kwargs), None)
        self.assertEqual(self.replacer.replace('new', args, kwargs),
                         (None, (1, 2), dict(callback='new')))

    def test_position(self):
        args = (1, 2, 'old', 3)
        kwargs = dict()
        self.assertEqual(self.replacer.get_old_value(args, kwargs), 'old')
        self.assertEqual(self.replacer.replace('new', args, kwargs),
                         ('old', [1, 2, 'new', 3], dict()))

    def test_keyword(self):
        args = (1, )
        kwargs = dict(y=2, callback='old', z=3)
        self.assertEqual(self.replacer.get_old_value(args, kwargs), 'old')
        self.assertEqual(self.replacer.replace('new', args, kwargs),
                         ('old', (1, ), dict(y=2, callback='new', z=3)))
Beispiel #10
0
class ArgReplacerTest(unittest.TestCase):
    def setUp(self):
        def function(x, y, callback=None, z=None):
            pass

        self.replacer = ArgReplacer(function, "callback")

    def test_omitted(self):
        args = (1, 2)
        kwargs = dict()  # type: Dict[str, Any]
        self.assertIs(self.replacer.get_old_value(args, kwargs), None)
        self.assertEqual(
            self.replacer.replace("new", args, kwargs),
            (None, (1, 2), dict(callback="new")),
        )

    def test_position(self):
        args = (1, 2, "old", 3)
        kwargs = dict()  # type: Dict[str, Any]
        self.assertEqual(self.replacer.get_old_value(args, kwargs), "old")
        self.assertEqual(
            self.replacer.replace("new", args, kwargs),
            ("old", [1, 2, "new", 3], dict()),
        )

    def test_keyword(self):
        args = (1,)
        kwargs = dict(y=2, callback="old", z=3)
        self.assertEqual(self.replacer.get_old_value(args, kwargs), "old")
        self.assertEqual(
            self.replacer.replace("new", args, kwargs),
            ("old", (1,), dict(y=2, callback="new", z=3)),
        )
Beispiel #11
0
 def setUp(self):
     def function(x, y, callback=None, z=None):
         pass
     self.replacer = ArgReplacer(function, 'callback')
Beispiel #12
0
def return_future(f):
    """Decorator to make a function that returns via callback return a
    `Future`.

    The wrapped function should take a ``callback`` keyword argument
    and invoke it with one argument when it has finished.  To signal failure,
    the function can simply raise an exception (which will be
    captured by the `.StackContext` and passed along to the ``Future``).

    From the caller's perspective, the callback argument is optional.
    If one is given, it will be invoked when the function is complete
    with `Future.result()` as an argument.  If the function fails, the
    callback will not be run and an exception will be raised into the
    surrounding `.StackContext`.

    If no callback is given, the caller should use the ``Future`` to
    wait for the function to complete (perhaps by yielding it in a
    `.gen.engine` function, or passing it to `.IOLoop.add_future`).

    Usage:

    .. testcode::

        @return_future
        def future_func(arg1, arg2, callback):
            # Do stuff (possibly asynchronous)
            callback(result)

        @gen.engine
        def caller(callback):
            yield future_func(arg1, arg2)
            callback()

    ..

    Note that ``@return_future`` and ``@gen.engine`` can be applied to the
    same function, provided ``@return_future`` appears first.  However,
    consider using ``@gen.coroutine`` instead of this combination.
    """
    replacer = ArgReplacer(f, 'callback')

    @functools.wraps(f)
    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

    return wrapper
Beispiel #13
0
    def setUp(self):
        def function(x, y, callback=None, z=None):
            pass

        self.replacer = ArgReplacer(function, "callback")
Beispiel #14
0
def return_future(f):
    """Decorator to make a function that returns via callback return a
    `Future`.

    装饰器, 通过 callback, 使 function 返回 一个 Future  

    The wrapped function should take a ``callback`` keyword argument
    and invoke it with one argument when it has finished.  To signal failure,
    the function can simply raise an exception (which will be
    captured by the `.StackContext` and passed along to the ``Future``).

    包装的 function 必须有一个 callback 关键字参数 而且在 function 结束时
    传入一个参数调用 callback. 为了通知 failure, 该 function 简单地 raise
    一个 exception (它会被 .StackContext 捕获, 并且被传递到 Future ).

    From the caller's perspective, the callback argument is optional.
    If one is given, it will be invoked when the function is complete
    with `Future.result()` as an argument.  If the function fails, the
    callback will not be run and an exception will be raised into the
    surrounding `.StackContext`.

    从 caller 的角度看, callback 参数是可选的. 如果给定, callback 会在 function
    完成时执行 Future.result() 作为参数被调用. 如果 function 执行失败, callback
    不会执行, 而且 exception 被 raised 进 .StackContext

    If no callback is given, the caller should use the ``Future`` to
    wait for the function to complete (perhaps by yielding it in a
    `.gen.engine` function, or passing it to `.IOLoop.add_future`).

    如果 没有 callback 指定, caller 调用者会使用 Future 等待 function 完成
    (可能被 yielding 在 .gen.engine function, 或者传递到 .IOLoop.add_future)

    Usage::

        @return_future
        def future_func(arg1, arg2, callback):
            # Do stuff (possibly asynchronous)
            callback(result)

        @gen.engine
        def caller(callback):
            yield future_func(arg1, arg2)
            callback()

    Note that ``@return_future`` and ``@gen.engine`` can be applied to the
    same function, provided ``@return_future`` appears first.  However,
    consider using ``@gen.coroutine`` instead of this combination.

    注意: @return_future 和 @gen.engine 可以应用于相同的 function,
    先使用 @return_future. 然而, 可以考虑使用 @gen.coroutine 替代以上组合.

    """
    replacer = ArgReplacer(f, 'callback')

    @functools.wraps(f)
    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

        # with 语句抛出的异常不会直接继续抛出, 因为 handle_error 一直返回 True,
        # ExceptionStackContext 类里的 __exit__ 函数处理异常时, 调用的是 handle_error
        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.
            # 4.1 继续执行 future.result()
            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()).
        """
        如果 caller 传递进一个 callback, 另它在 future 解析时被调用.
        在我们返回 future 之前, 这是非常重要, 否则我们冒险地将多个 exception
        混淆了 stack context(这里一个紧急的 exception, 而 future 解析时又一个, 且
        callback 在调用 future.result() 时出发了它自己的 exception )
        """
        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

    return wrapper
Beispiel #15
0
def return_future(f):
    """Decorator to make a function that returns via callback return a `Future`.

    The wrapped function should take a ``callback`` keyword argument
    and invoke it with one argument when it has finished.  To signal failure,
    the function can simply raise an exception (which will be
    captured by the `stack_context` and passed along to the `Future`).

    From the caller's perspective, the callback argument is optional.
    If one is given, it will be invoked when the function is complete
    with the `Future` as an argument.  If no callback is given, the caller
    should use the `Future` to wait for the function to complete
    (perhaps by yielding it in a `gen.engine` function, or passing it
    to `IOLoop.add_future`).

    Usage::
        @return_future
        def future_func(arg1, arg2, callback):
            # Do stuff (possibly asynchronous)
            callback(result)

        @gen.engine
        def caller(callback):
            yield future_func(arg1, arg2)
            callback()

    Note that ``@return_future`` and ``@gen.engine`` can be applied to the
    same function, provided ``@return_future`` appears first.
    """
    replacer = ArgReplacer(f, 'callback')

    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        future = Future()
        callback, args, kwargs = replacer.replace(future.set_result, args,
                                                  kwargs)
        if callback is not None:
            future.add_done_callback(callback)

        def handle_error(typ, value, tb):
            future.set_exception(value)
            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.
            raise_exc_info(exc_info)
        return future

    return wrapper