# Path: asynq/contexts.py
# class AsyncContext(object):
#     """Base class for contexts that should pause and resume during an async's function execution.
# 
#     Your context should subclass this class and implement pause and resume (at least).
# 
#     That would make the context pause and resume each time the execution of the async function
#     within this context is paused and resumed.
# 
#     Additionally, you can also subclass __enter__ and __exit__ if you want to customize its
#     behaviour. Remember to call super in that case.
# 
#     NOTE: __enter__/__exit__ methods automatically call resume/pause so the overridden
#     __enter__/__exit__ methods shouldn't do that explicitly.
# 
#     """
# 
#     def __enter__(self):
#         self._active_task = enter_context(self)
#         self.resume()
#         return self
# 
#     def __exit__(self, ty, value, tb):
#         leave_context(self, self._active_task)
#         self.pause()
#         del self._active_task
# 
#     def resume(self):
#         raise NotImplementedError()
# 
#     def pause(self):
#         raise NotImplementedError()
# 
# Path: asynq/decorators.py
# def asynq(self, *args, **kwargs):
#     if self.instance is None:
#         return self.decorator.asynq(*args, **kwargs)
#     else:
#         return self.decorator.asynq(self.instance, *args, **kwargs)
# 
# @async_proxy()
# def async_call(fn, *args, **kwargs):
#     """Use this if you are not sure if fn is async or not.
# 
#     e.g. when you are within an async function and you need to call fn but it could either be
#     async or non-async, you should write
#     val = yield async_call.asynq(fn, arg1, kw1=value1)
# 
#     """
#     if is_pure_async_fn(fn):
#         return fn(*args, **kwargs)
#     elif hasattr(fn, "asynq"):
#         return fn.asynq(*args, **kwargs)
#     elif hasattr(fn, "async"):
#         return getattr(fn, "async")(*args, **kwargs)
#     else:
#         return futures.ConstFuture(fn(*args, **kwargs))
# 
# class AsyncDecorator(PureAsyncDecorator):
#     binder_cls = AsyncDecoratorBinder
# 
#     def is_pure_async_fn(self):
#         return False
# 
#     def asynq(self, *args, **kwargs):
#         return self._call_pure(args, kwargs)
# 
#     def name(self):
#         return "@asynq()"
# 
#     def __call__(self, *args, **kwargs):
#         return self._call_pure(args, kwargs).value()
# 
# class AsyncDecoratorBinder(qcore.decorators.DecoratorBinder):
#     def asynq(self, *args, **kwargs):
#         if self.instance is None:
#             return self.decorator.asynq(*args, **kwargs)
#         else:
#             return self.decorator.asynq(self.instance, *args, **kwargs)


from .contexts import AsyncContext
from .decorators import asynq, async_call, AsyncDecorator, AsyncDecoratorBinder
from qcore import get_original_fn, utime
from qcore.caching import get_args_tuple, get_kwargs_defaults, LRUCache
from qcore.events import EventHook
from qcore.errors import reraise, prepare_for_reraise
from qcore.decorators import decorate
import functools
import inspect
import itertools
import weakref
import threading
import time
@asynq()
def call_with_context(context, fn, *args, **kwargs):
    """Calls fn in the given with context.

    This is useful if you need to call two functions at once, but only one should be called in the
    context. For example:

        important, not_important = yield (
            get_important.asynq(oid),
            call_with_context.asynq(a.livenode.dep.IgnoreDependencies(), get_not_important, oid).
        )

    """
    with context:
        return (yield fn.asynq(*args, **kwargs))


class DeduplicateDecoratorBinder(AsyncDecoratorBinder):
    def dirty(self, *args, **kwargs):
        if self.instance is None:
            self.decorator.dirty(*args, **kwargs)
        else:
            self.decorator.dirty(self.instance, *args, **kwargs)


class DeduplicateDecorator(AsyncDecorator):
    binder_cls = DeduplicateDecoratorBinder
    tasks = {}

    def __init__(self, fn, task_cls, keygetter):

# Path: asynq/contexts.py
# class AsyncContext(object):
#     """Base class for contexts that should pause and resume during an async's function execution.
# 
#     Your context should subclass this class and implement pause and resume (at least).
# 
#     That would make the context pause and resume each time the execution of the async function
#     within this context is paused and resumed.
# 
#     Additionally, you can also subclass __enter__ and __exit__ if you want to customize its
#     behaviour. Remember to call super in that case.
# 
#     NOTE: __enter__/__exit__ methods automatically call resume/pause so the overridden
#     __enter__/__exit__ methods shouldn't do that explicitly.
# 
#     """
# 
#     def __enter__(self):
#         self._active_task = enter_context(self)
#         self.resume()
#         return self
# 
#     def __exit__(self, ty, value, tb):
#         leave_context(self, self._active_task)
#         self.pause()
#         del self._active_task
# 
#     def resume(self):
#         raise NotImplementedError()
# 
#     def pause(self):
#         raise NotImplementedError()
# 
# Path: asynq/decorators.py
# def asynq(self, *args, **kwargs):
#     if self.instance is None:
#         return self.decorator.asynq(*args, **kwargs)
#     else:
#         return self.decorator.asynq(self.instance, *args, **kwargs)
# 
# @async_proxy()
# def async_call(fn, *args, **kwargs):
#     """Use this if you are not sure if fn is async or not.
# 
#     e.g. when you are within an async function and you need to call fn but it could either be
#     async or non-async, you should write
#     val = yield async_call.asynq(fn, arg1, kw1=value1)
# 
#     """
#     if is_pure_async_fn(fn):
#         return fn(*args, **kwargs)
#     elif hasattr(fn, "asynq"):
#         return fn.asynq(*args, **kwargs)
#     elif hasattr(fn, "async"):
#         return getattr(fn, "async")(*args, **kwargs)
#     else:
#         return futures.ConstFuture(fn(*args, **kwargs))
# 
# class AsyncDecorator(PureAsyncDecorator):
#     binder_cls = AsyncDecoratorBinder
# 
#     def is_pure_async_fn(self):
#         return False
# 
#     def asynq(self, *args, **kwargs):
#         return self._call_pure(args, kwargs)
# 
#     def name(self):
#         return "@asynq()"
# 
#     def __call__(self, *args, **kwargs):
#         return self._call_pure(args, kwargs).value()
# 
# class AsyncDecoratorBinder(qcore.decorators.DecoratorBinder):
#     def asynq(self, *args, **kwargs):
#         if self.instance is None:
#             return self.decorator.asynq(*args, **kwargs)
#         else:
#             return self.decorator.asynq(self.instance, *args, **kwargs)


# Path: asynq/tools.py
from .contexts import AsyncContext
from .decorators import asynq, async_call, AsyncDecorator, AsyncDecoratorBinder
from qcore import get_original_fn, utime
from qcore.caching import get_args_tuple, get_kwargs_defaults, LRUCache
from qcore.events import EventHook
from qcore.errors import reraise, prepare_for_reraise
from qcore.decorators import decorate
import functools
import inspect
import itertools
import weakref
import threading
import time

@asynq()
def call_with_context(context, fn, *args, **kwargs):
    """Calls fn in the given with context.

    This is useful if you need to call two functions at once, but only one should be called in the
    context. For example:

        important, not_important = yield (
            get_important.asynq(oid),
            call_with_context.asynq(a.livenode.dep.IgnoreDependencies(), get_not_important, oid).
        )

    """
    with context:
        return (yield fn.asynq(*args, **kwargs))


class DeduplicateDecoratorBinder(AsyncDecoratorBinder):
    def dirty(self, *args, **kwargs):
        if self.instance is None:
            self.decorator.dirty(*args, **kwargs)
        else:
            self.decorator.dirty(self.instance, *args, **kwargs)


class DeduplicateDecorator(AsyncDecorator):
    binder_cls = DeduplicateDecoratorBinder
    tasks = {}

    def __init__(self, fn, task_cls, keygetter):

        AsyncDecorator.__init__(self, fn, task_cls)