Esempio n. 1
0
    def decorator(fn):
        cache = LRUCache(maxsize)
        argspec = inspect2.getfullargspec(get_original_fn(fn))
        arg_names = argspec.args[1:] + argspec.kwonlyargs  # remove self
        async_fun = fn.asynq
        kwargs_defaults = get_kwargs_defaults(argspec)

        cache_key = key_fn
        if cache_key is None:

            def cache_key(args, kwargs):
                return get_args_tuple(args, kwargs, arg_names, kwargs_defaults)

        @asynq()
        @functools.wraps(fn)
        def wrapper(*args, **kwargs):
            key = cache_key(args, kwargs)
            try:
                result(cache[key])
                return
            except KeyError:
                value = yield async_fun(*args, **kwargs)
                cache[key] = value
                result(value)
                return

        return wrapper
Esempio n. 2
0
    def test_retry_preserves_argspec(self):
        def fn(foo, bar, baz=None, **kwargs):
            pass

        decorated = aretry(Exception)(fn)

        assert_eq(inspect.getargspec(fn),
                  inspect.getargspec(get_original_fn(decorated)))
Esempio n. 3
0
    def decorator(fun):
        _keygetter = keygetter
        if _keygetter is None:
            original_fn = get_original_fn(fun)
            argspec = inspect2.getfullargspec(original_fn)
            arg_names = argspec.args + argspec.kwonlyargs
            kwargs_defaults = get_kwargs_defaults(argspec)
            _keygetter = lambda args, kwargs: get_args_tuple(
                args, kwargs, arg_names, kwargs_defaults)

        return decorate(DeduplicateDecorator, fun.task_cls, _keygetter)(fun)
Esempio n. 4
0
    def cache_fun(fun):
        argspec = inspect2.getfullargspec(get_original_fn(fun))
        arg_names = argspec.args[1:] + argspec.kwonlyargs  # remove self
        async_fun = fun.asynq
        kwargs_defaults = get_kwargs_defaults(argspec)
        cache = {}

        def cache_key(args, kwargs):
            return get_args_tuple(args, kwargs, arg_names, kwargs_defaults)

        def clear_cache(instance_key, ref):
            del cache[instance_key]

        @async_proxy()
        @functools.wraps(fun)
        def new_fun(self, *args, **kwargs):
            instance_key = id(self)
            if instance_key not in cache:
                ref = weakref.ref(self,
                                  functools.partial(clear_cache, instance_key))
                cache[instance_key] = (ref, {})
            instance_cache = cache[instance_key][1]

            k = cache_key(args, kwargs)
            try:
                return ConstFuture(instance_cache[k])
            except KeyError:

                def callback(task):
                    instance_cache[k] = task.value()

                task = async_fun(self, *args, **kwargs)
                task.on_computed.subscribe(callback)
                return task

        # just so unit tests can check that this is cleaned up correctly
        new_fun.__acached_per_instance_cache__ = cache
        return new_fun
Esempio n. 5
0
    def cache_fun(fun):
        argspec = inspect2.getfullargspec(get_original_fn(fun))
        arg_names = argspec.args[1:] + argspec.kwonlyargs  # remove self
        async_fun = fun.asynq
        kwargs_defaults = get_kwargs_defaults(argspec)
        cache = {}

        def cache_key(args, kwargs):
            return get_args_tuple(args, kwargs, arg_names, kwargs_defaults)

        def clear_cache(instance_key, ref):
            del cache[instance_key]

        @asynq()
        @functools.wraps(fun)
        def new_fun(self, *args, **kwargs):
            instance_key = id(self)
            if instance_key not in cache:
                ref = weakref.ref(self,
                                  functools.partial(clear_cache, instance_key))
                cache[instance_key] = (ref, {})
            instance_cache = cache[instance_key][1]

            k = cache_key(args, kwargs)
            try:
                result(instance_cache[k])
                return
            except KeyError:
                value = yield async_fun(self, *args, **kwargs)
                instance_cache[k] = value
                result(value)
                return

        # just so unit tests can check that this is cleaned up correctly
        new_fun.__acached_per_instance_cache__ = cache
        return new_fun
Esempio n. 6
0
    def _uncached_get_argspec(self, obj: Any, impl: Optional[Impl],
                              is_asynq: bool) -> MaybeSignature:
        if isinstance(obj, tuple) or hasattr(obj, "__getattr__"):
            return None  # lost cause

        # Cythonized methods, e.g. fn.asynq
        if is_dot_asynq_function(obj):
            try:
                return self._cached_get_argspec(obj.__self__, impl, is_asynq)
            except TypeError:
                # some cythonized methods have __self__ but it is not a function
                pass

        # for bound methods, see if we have an argspec for the unbound method
        if inspect.ismethod(obj) and obj.__self__ is not None:
            argspec = self._cached_get_argspec(obj.__func__, impl, is_asynq)
            return make_bound_method(argspec, KnownValue(obj.__self__))

        if hasattr(obj, "fn") or hasattr(obj, "original_fn"):
            is_asynq = is_asynq or hasattr(obj, "asynq")
            # many decorators put the original function in the .fn attribute
            try:
                original_fn = qcore.get_original_fn(obj)
            except (TypeError, AttributeError):
                # fails when executed on an object that doesn't allow setting attributes,
                # e.g. certain extension classes
                pass
            else:
                return self._cached_get_argspec(original_fn, impl, is_asynq)

        argspec = self.ts_finder.get_argspec(obj)
        if argspec is not None:
            return argspec

        if inspect.isfunction(obj):
            if hasattr(obj, "inner"):
                # @qclient.task_queue.exec_after_request() puts the original function in .inner
                return self._cached_get_argspec(obj.inner, impl, is_asynq)

            # NewTypes, but we don't currently know how to handle NewTypes over more
            # complicated types.
            if hasattr(obj, "__supertype__") and isinstance(
                    obj.__supertype__, type):
                # NewType
                return Signature.make(
                    [
                        SigParameter(
                            "x",
                            SigParameter.POSITIONAL_ONLY,
                            annotation=type_from_runtime(
                                obj.__supertype__, ctx=self.default_context),
                        )
                    ],
                    NewTypeValue(obj),
                    callable=obj,
                )

            inspect_sig = self._safe_get_signature(obj)
            if inspect_sig is None:
                return self._make_any_sig(obj)

            return self.from_signature(
                inspect_sig,
                function_object=obj,
                is_async=asyncio.iscoroutinefunction(obj),
                impl=impl,
                is_asynq=is_asynq,
            )

        # decorator binders
        if _is_qcore_decorator(obj):
            argspec = self._cached_get_argspec(obj.decorator, impl, is_asynq)
            # wrap if it's a bound method
            if obj.instance is not None and argspec is not None:
                return make_bound_method(argspec, KnownValue(obj.instance))
            return argspec

        if inspect.isclass(obj):
            obj = self.config.unwrap_cls(obj)
            override = self.config.get_constructor(obj)
            if isinstance(override, Signature):
                signature = override
            else:
                should_ignore = safe_in(obj, self.config.IGNORED_CALLEES)
                return_type = UNRESOLVED_VALUE if should_ignore else TypedValue(
                    obj)
                allow_call = safe_issubclass(
                    obj, self.config.CLASSES_SAFE_TO_INSTANTIATE)
                if isinstance(override, inspect.Signature):
                    inspect_sig = override
                else:
                    if override is not None:
                        constructor = override
                    elif hasattr(obj, "__init__"):
                        constructor = obj.__init__
                    else:
                        # old-style class
                        return Signature.make(
                            [],
                            return_type,
                            is_ellipsis_args=True,
                            callable=obj,
                            allow_call=allow_call,
                        )
                    inspect_sig = self._safe_get_signature(constructor)
                if inspect_sig is None:
                    return Signature.make(
                        [],
                        return_type,
                        is_ellipsis_args=True,
                        callable=obj,
                        allow_call=allow_call,
                    )

                signature = self.from_signature(
                    inspect_sig,
                    function_object=obj,
                    impl=impl,
                    returns=return_type,
                    allow_call=allow_call,
                )
            bound_sig = make_bound_method(signature, TypedValue(obj))
            if bound_sig is None:
                return None
            sig = bound_sig.get_signature(preserve_impl=True)
            if sig is not None:
                return sig
            return bound_sig

        if inspect.isbuiltin(obj):
            if hasattr(obj, "__self__") and not isinstance(
                    obj.__self__, ModuleType):
                cls = type(obj.__self__)
                try:
                    method = getattr(cls, obj.__name__)
                except AttributeError:
                    return self._make_any_sig(obj)
                if method == obj:
                    return self._make_any_sig(obj)
                argspec = self._cached_get_argspec(method, impl, is_asynq)
                return make_bound_method(argspec, KnownValue(obj.__self__))
            inspect_sig = self._safe_get_signature(obj)
            if inspect_sig is not None:
                return self.from_signature(inspect_sig, function_object=obj)
            return self._make_any_sig(obj)

        if hasattr(obj, "__call__"):
            # we could get an argspec here in some cases, but it's impossible to figure out
            # the argspec for some builtin methods (e.g., dict.__init__), and no way to detect
            # these with inspect, so just give up.
            return self._make_any_sig(obj)

        if isinstance(obj, property):
            # If we know the getter, inherit its return value.
            if obj.fget:
                fget_argspec = self._cached_get_argspec(
                    obj.fget, impl, is_asynq)
                if fget_argspec is not None and fget_argspec.has_return_value(
                ):
                    return PropertyArgSpec(
                        obj, return_value=fget_argspec.return_value)
            return PropertyArgSpec(obj)

        return None