def find_class():
    """ This scope is special, because its in front of TestClass """
    #? ['ret']
    TestClass.ret
    if 1:
        #? ['ret']
        TestClass.ret

class FindClass():
    #? []
    TestClass.ret
    if a:
        #? []
        TestClass.ret

    def find_class(self):
        #? ['ret']
        TestClass.ret
        if 1:
            #? ['ret']
            TestClass.ret

#? []
FindClass().find_class.self
#? []
FindClass().find_class.self.find_class

# set variables, which should not be included, because they don't belong to the
# class
second = 1
second = ""
class TestClass(object):
    var_class = TestClass(1)
    self.pseudo_var = 3

    def __init__(self2, first_param, second_param, third=1.0):
        self2.var_inst = first_param
        self2.second = second_param
        self2.first = first_param
        self2.first.var_on_argument = 5
        a = 3

    def var_func(self):
        return 1

    def get_first(self):
        # traversal
        self.second_new = self.second
        return self.var_inst

    def values(self):
        self.var_local = 3
        #? ['var_class', 'var_func', 'var_inst', 'var_local']
        self.var_
        #?
        var_local

    def ret(self, a1):
        # should not know any class functions!
        #? []
        values
        #?
        values
        #? ['return']
        ret
        return a1

# should not work
#? []
var_local
#? []
var_inst
#? []
var_func

# instance
inst = TestClass(1)

#? ['var_class', 'var_func', 'var_inst', 'var_local']
inst.var

#? ['var_class', 'var_func']
TestClass.var

#? int()
inst.var_local
#? []
TestClass.var_local.
#?
TestClass.pseudo_var
#?
TestClass().pseudo_var

#? int()
TestClass().ret(1)
# Should not return int(), because we want the type before `.ret(1)`.
#? 11 TestClass()
TestClass().ret(1)
#? int()
inst.ret(1)

myclass = TestClass(1, '', 3.0)
#? int()
myclass.get_first()
#? []
myclass.get_first.real

# too many params
#? int()
TestClass(1,1,1).var_inst

# too few params
#? int()
TestClass(1).first
#? []
TestClass(1).second.

# complicated variable settings in class
#? str()
myclass.second
#? str()
myclass.second_new

# multiple classes / ordering
ints = TestClass(1, 1.0)
strs = TestClass("", '')
#? float()
ints.second
#? str()
strs.second

#? ['var_class']
TestClass.var_class.var_class.var_class.var_class

# operations (+, *, etc) shouldn't be InstanceElements - #246
class A():
    def __init__(self):
        self.addition = 1 + 2
#? int()
A().addition

# should also work before `=`
#? 8 int()
A().addition = None
#? 8 int()
A(1).addition = None
#? 1 A
A(1).addition = None
a = A()
#? 8 int()
a.addition = None


# -----------------
# inheritance
# -----------------

class Base(object):
    def method_base(self):
        return 1

class SuperClass(Base):
    class_super = 3
    def __init__(self):
        self.var_super = ''
    def method_super(self):
        self.var2_super = list

class Mixin(SuperClass):
    def method_mixin(self):
        return int

#? 20 SuperClass
class SubClass(SuperClass):
    class_sub = 3
    def __init__(self):
        self.var_sub = ''
    def method_sub(self):
        self.var_sub = list
        return tuple

instance = SubClass()

#? ['method_base', 'method_sub', 'method_super']
instance.method_
#? ['var2_super', 'var_sub', 'var_super']
instance.var
#? ['class_sub', 'class_super']
instance.class_

#? ['method_base', 'method_sub', 'method_super']
SubClass.method_
#? []
SubClass.var
#? ['class_sub', 'class_super']
SubClass.class_

# -----------------
# inheritance of builtins
# -----------------

class Base(str):
    pass

#? ['upper']
Base.upper
#? ['upper']
Base().upper

# -----------------
# dynamic inheritance
# -----------------

class Angry(object):
    def shout(self):
        return 'THIS IS MALARKEY!'

def classgetter():
    return Angry

class Dude(classgetter()):
    def react(self):
        #? ['shout']
        self.s

# -----------------
# multiple inheritance # 1071
# -----------------

class FactorMixin(object):
    FACTOR_1 = 0.1

class Calc(object):
    def sum(self, a, b):
        self.xxx = 3
        return a + b

class BetterCalc(Calc, FactorMixin):
    def multiply_factor(self, a):
        return a * self.FACTOR_1

calc = BetterCalc()
#? ['sum']
calc.sum
#? ['multiply_factor']
calc.multip
#? ['FACTOR_1']
calc.FACTOR_1
#? ['xxx']
calc.xxx

# -----------------
# __call__
# -----------------

class CallClass():
    def __call__(self):
        return 1

#? int()
CallClass()()

# -----------------
# variable assignments
# -----------------

class V:
    def __init__(self, a):
        self.a = a

    def ret(self):
        return self.a

    d = b
    b = ret
    if 1:
        c = b

#? int()
V(1).b()
#? int()
V(1).c()
#?
V(1).d()
# Only keywords should be possible to complete.
#? ['is', 'in', 'not', 'and', 'or', 'if']
V(1).d() 


# -----------------
# ordering
# -----------------
class A():
    def b(self):
        #? int()
        a_func()
        #? str()
        self.a_func()
        return a_func()

    def a_func(self):
        return ""

def a_func():
    return 1

#? int()
A().b()
#? str()
A().a_func()

# -----------------
# nested classes
# -----------------
class A():
    class B():
        pass
    def b(self):
        return 1.0

#? float()
A().b()

class A():
    def b(self):
        class B():
            def b(self):
                return []
        return B().b()

#? list()
A().b()

# -----------------
# ducktyping
# -----------------

def meth(self):
    return self.a, self.b

class WithoutMethod():
    a = 1
    def __init__(self):
        self.b = 1.0
    def blub(self):
        return self.b
    m = meth

class B():
    b = ''

a = WithoutMethod().m()
#? int()
a[0]
#? float()
a[1]

#? float()
WithoutMethod.blub(WithoutMethod())
#? str()
WithoutMethod.blub(B())

# -----------------
# __getattr__ / getattr() / __getattribute__
# -----------------

#? str().upper
getattr(str(), 'upper')
#? str.upper
getattr(str, 'upper')

# some strange getattr calls
#?
getattr(str, 1)
#?
getattr()
#?
getattr(str)
#?
getattr(getattr, 1)
#?
getattr(str, [])


# python >= 3.5
class Base():
    def ret(self, b):
        return b

class Wrapper():
    def __init__(self, obj):
        self.obj = obj

    def __getattr__(self, name):
        return getattr(self.obj, name)

class Wrapper2():
    def __getattribute__(self, name):
        return getattr(Base(), name)

#? int()
Wrapper(Base()).ret(3)
#? ['ret']
Wrapper(Base()).ret
#? int()
Wrapper(Wrapper(Base())).ret(3)
#? ['ret']
Wrapper(Wrapper(Base())).ret

#? int()
Wrapper2(Base()).ret(3)

class GetattrArray():
    def __getattr__(self, name):
        return [1]

#? int()
GetattrArray().something[0]
#? []
GetattrArray().something

class WeirdGetattr:
    class __getattr__():
        pass

#? []
WeirdGetattr().something


# -----------------
# private vars
# -----------------
class PrivateVar():
    def __init__(self):
        self.__var = 1
        #? int()
        self.__var
        #? ['__var']
        self.__var

    def __private_func(self):
        return 1

    #? int()
    __private_func()

    def wrap_private(self):
        return self.__private_func()
#? []
PrivateVar().__var
#?
PrivateVar().__var
#? []
PrivateVar().__private_func
#? []
PrivateVar.__private_func
#? int()
PrivateVar().wrap_private()


class PrivateSub(PrivateVar):
    def test(self):
        #? []
        self.__var

    def wrap_private(self):
        #? []
        self.__var

#? []
PrivateSub().__var

# -----------------
# super
# -----------------
class Super(object):
    a = 3
    def return_sup(self):
        return 1
SuperCopy = Super

class TestSuper(Super):
    #?
    super()
    def test(self):
        #? SuperCopy()
        super()
        #? ['a']
        super().a
        if 1:
            #? SuperCopy()
            super()
        def a():
            #?
            super()

    def return_sup(self):
        #? int()
        return super().return_sup()

#? int()
TestSuper().return_sup()


Super = 3

class Foo():
    def foo(self):
        return 1
# Somehow overwriting the same name caused problems (#1044)
class Foo(Foo):
    def foo(self):
        #? int()
        super().foo()

# -----------------
# if flow at class level
# -----------------
class TestX(object):
    def normal_method(self):
        return 1

    if True:
        def conditional_method(self):
            var = self.normal_method()
            #? int()
            var
            return 2

    def other_method(self):
        var = self.conditional_method()
        #? int()
        var

# -----------------
# mro method
# -----------------

class A(object):
    a = 3

#? ['mro']
A.mro
#? []
A().mro


# -----------------
# mro resolution
# -----------------

class B(A()):
    b = 3

#?
B.a
#?
B().a
#? int()
B.b
#? int()
B().b


# -----------------
# With import
# -----------------

from import_tree.classes import Config2, BaseClass

class Config(BaseClass):
    """#884"""

#? Config2()
Config.mode

#? int()
Config.mode2


# -----------------
# Nested class/def/class
# -----------------
class Foo(object):
    a = 3
    def create_class(self):
        class X():
            a = self.a
            self.b = 3.0
        return X

#? int()
Foo().create_class().a
#? float()
Foo().b

class Foo(object):
    def comprehension_definition(self):
        return [1 for self.b in [1]]

#? int()
Foo().b

# -----------------
# default arguments
# -----------------

default = ''
class DefaultArg():
    default = 3
    def x(self, arg=default):
        #? str()
        default
        return arg
    def y(self):
        return default

#? int()
DefaultArg().x()
#? str()
DefaultArg().y()
#? int()
DefaultArg.x()
#? str()
DefaultArg.y()


# -----------------
# Error Recovery
# -----------------

from import_tree.pkg.base import MyBase

class C1(MyBase):
    def f3(self):
        #! 13 ['def f1']
        self.f1() . # hey'''
        #? 13 MyBase.f1
        self.f1() . # hey'''

# -----------------
# With a very weird __init__
# -----------------

class WithWeirdInit:
    class __init__:
        def __init__(self, a):
            self.a = a

    def y(self):
        return self.a


#?
WithWeirdInit(1).y()
