# -----------------
# normal decorators
# -----------------

def decorator(func):
    def wrapper(*args):
        return func(1, *args)
    return wrapper

@decorator
def decorated(a,b):
    return a,b

exe = decorated(set, '')

#? set
exe[1]

#? int()
exe[0]

# more complicated with args/kwargs
def dec(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@dec
def fu(a, b, c, *args, **kwargs):
    return a, b, c, args, kwargs

exe = fu(list, c=set, b=3, d='')

#? list
exe[0]
#? int()
exe[1]
#? set
exe[2]
#? []
exe[3][0].
#? str()
exe[4]['d']


exe = fu(list, set, 3, '', d='')

#? str()
exe[3][0]

# -----------------
# multiple decorators
# -----------------
def dec2(func2):
    def wrapper2(first_arg, *args2, **kwargs2):
        return func2(first_arg, *args2, **kwargs2)
    return wrapper2

@dec2
@dec
def fu2(a, b, c, *args, **kwargs):
    return a, b, c, args, kwargs

exe = fu2(list, c=set, b=3, d='str')

#? list
exe[0]
#? int()
exe[1]
#? set
exe[2]
#? []
exe[3][0].
#? str()
exe[4]['d']


# -----------------
# Decorator is a class
# -----------------
def same_func(func):
    return func

class Decorator(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        return self.func(1, *args, **kwargs)

@Decorator
def nothing(a,b,c):
    return a,b,c

#? int()
nothing("")[0]
#? str()
nothing("")[1]


@same_func
@Decorator
def nothing(a,b,c):
    return a,b,c

#? int()
nothing("")[0]

class MethodDecoratorAsClass():
    class_var = 3
    @Decorator
    def func_without_self(arg, arg2):
        return arg, arg2

    @Decorator
    def func_with_self(self, arg):
        return self.class_var

#? int()
MethodDecoratorAsClass().func_without_self('')[0]
#? str()
MethodDecoratorAsClass().func_without_self('')[1]
#? 
MethodDecoratorAsClass().func_with_self(1)


class SelfVars():
    """Init decorator problem as an instance, #247"""
    @Decorator
    def __init__(self):
        """
        __init__ decorators should be ignored when looking up variables in the
        class.
        """
        self.c = list

    @Decorator
    def shouldnt_expose_var(not_self):
        """
        Even though in real Python this shouldn't expose the variable, in this
        case Jedi exposes the variable, because these kind of decorators are
        normally descriptors, which SHOULD be exposed (at least 90%).
        """
        not_self.b = 1.0

    def other_method(self):
        #? float()
        self.b
        #? list
        self.c

# -----------------
# not found decorators (are just ignored)
# -----------------
@not_found_decorator
def just_a_func():
    return 1

#? int()
just_a_func()

#? ['__closure__']
just_a_func.__closure__


class JustAClass:
    @not_found_decorator2
    def a(self):
        return 1

#? ['__call__']
JustAClass().a.__call__
#? int()
JustAClass().a()
#? ['__call__']
JustAClass.a.__call__
#? int()
JustAClass.a()

# -----------------
# illegal decorators
# -----------------

class DecoratorWithoutCall():
    def __init__(self, func):
        self.func = func

@DecoratorWithoutCall
def f():
    return 1

# cannot be resolved - should be ignored
@DecoratorWithoutCall(None)
def g():
    return 1

#? 
f()
#? int()
g()


class X():
    @str
    def x(self):
        pass

    def y(self):
        #? str()
        self.x
        #?
        self.x()


def decorator_var_args(function, *args):
    return function(*args)

@decorator_var_args
def function_var_args(param):
    return param

#? int()
function_var_args(1)

# -----------------
# method decorators
# -----------------

def dec(f):
    def wrapper(s):
        return f(s)
    return wrapper

class MethodDecorators():
    _class_var = 1
    def __init__(self):
        self._method_var = ''

    @dec
    def constant(self):
        return 1.0

    @dec
    def class_var(self):
        return self._class_var

    @dec
    def method_var(self):
        return self._method_var

#? float()
MethodDecorators().constant()
#? int()
MethodDecorators().class_var()
#? str()
MethodDecorators().method_var()


class Base():
    @not_existing
    def __init__(self):
        pass
    @not_existing
    def b(self):
        return ''
    @dec
    def c(self):
        return 1

class MethodDecoratorDoesntExist(Base):
    """#272 github: combination of method decorators and super()"""
    def a(self):
        #? 
        super().__init__()
        #? str()
        super().b()
        #? int()
        super().c()
        #? float()
        self.d()

    @doesnt_exist
    def d(self):
        return 1.0

# -----------------
# others
# -----------------
def memoize(function):
        def wrapper(*args):
            if random.choice([0, 1]):
                pass
            else:
                rv = function(*args)
                return rv
        return wrapper

@memoize
def follow_statement(stmt):
    return stmt

# here we had problems with the else clause, because the parent was not right.
#? int()
follow_statement(1)

# -----------------
# class decorators
# -----------------

# class decorators should just be ignored
@should_ignore
class A():
    def ret(self):
        return 1

#? int()
A().ret()


# -----------------
# On decorator completions
# -----------------

import abc
#? ['abc']
@abc

#? ['abstractmethod']
@abc.abstractmethod
