# from functools import wraps


# def debugMethod(func, cls=None):
#     '''decorator for debugging passed function'''
#     @wraps(func)
#     def wrapper(*args, **kwargs):
#         print({
#             'class': cls,  # class object, not string
#             'method': func.__qualname__,  # method name, string
#             'args': args,  # all args, including self
#             'kwargs': kwargs
#         })
#         return func(*args, **kwargs)
#     return wrapper


class Meta(type):
    # def __new__(cls, clsname, bases, clsdict):
    #    obj = super().__new__(cls, clsname, bases, clsdict)
    #    def IS_DUNDER(x): return x.startswith("__") and x.endswith("__")
    #    for item, val in vars(obj).items():
    #        if callable(val):
    #            # setattr(obj, item, debugMethod(val, cls=cls))
    #            pass
    #    return obj

    def __getattribute__(self, name):
        print({'action': "get", 'attr': name})
        return object.__getattribute__(self, name)


class A(object, metaclass=Meta):
    # def __init__(self):
    #     pass
    # def __setattr__(self, name, value):
    #     print("setting attribute", name, value)
    #     return super().__setattr__(name, value)

    # def __getattribute__(self, name):
    #     print("getting attribute", name)
    #     return super().__getattribute__(name)

    def add(self, x, y):
        return x+y

    def mul(self, x, y):
        return x*y


a = A()
# a.mul(2, 3)
# a.mul(0, 100)
# a.mul(2, 100)
# a.mul(2323, 3)
# a.mul(2, 2)
# a.mul(2, 3414)

# another setter
a.x = 200
# print(vars(a))

# # settter
# a.x = 1000

# # setter
# a.x = "23931903"

# # finally a getter
# my_getter_val = a.x

# from functools import wraps


# def debugMethod(func, cls=None):
#     '''decorator for debugging passed function'''
#     @wraps(func)
#     def wrapper(*args, **kwargs):
#         print({
#             'class': cls,  # class object, not string
#             'method': func.__qualname__,  # method name, string
#             'args': args,  # all args, including self
#             'kwargs': kwargs
#         })
#         return func(*args, **kwargs)
#     return wrapper


class Meta(type):
    # def __new__(cls, clsname, bases, clsdict):
    #    obj = super().__new__(cls, clsname, bases, clsdict)
    #    def IS_DUNDER(x): return x.startswith("__") and x.endswith("__")
    #    for item, val in vars(obj).items():
    #        if callable(val):
    #            # setattr(obj, item, debugMethod(val, cls=cls))
    #            pass
    #    return obj

    def __getattribute__(self, name):
        print({'action': "get", 'attr': name})
        return object.__getattribute__(self, name)


class A(object, metaclass=Meta):
    # def __init__(self):
    #     pass
    # def __setattr__(self, name, value):
    #     print("setting attribute", name, value)
    #     return super().__setattr__(name, value)

    # def __getattribute__(self, name):
    #     print("getting attribute", name)
    #     return super().__getattribute__(name)

    def add(self, x, y):
        return x+y

    def mul(self, x, y):
        return x*y


a = A()
# a.mul(2, 3)
# a.mul(0, 100)
# a.mul(2, 100)
# a.mul(2323, 3)
# a.mul(2, 2)
# a.mul(2, 3414)

# another setter
a.x = 200
# print(vars(a))

# # settter
# a.x = 1000

# # setter
# a.x = "23931903"

# # finally a getter
# my_getter_val = a.x

# from functools import wraps # def debugMethod(func, cls=None): #     '''decorator for debugging passed function''' #     @wraps(func) #     def wrapper(*args, **kwargs): #         print({ #             'class': cls,  # class object, not string #             'method': func.__qualname__,  # method name, string #             'args': args,  # all args, including self #             'kwargs': kwargs #         }) #         return func(*args, **kwargs) #     return wrapper # def __new__(cls, clsname, bases, clsdict): #    obj = super().__new__(cls, clsname, bases, clsdict) #    def IS_DUNDER(x): return x.startswith("__") and x.endswith("__") #    for item, val in vars(obj).items(): #        if callable(val): #            # setattr(obj, item, debugMethod(val, cls=cls)) #            pass #    return obj # def __init__(self): #     pass # def __setattr__(self, name, value): #     print("setting attribute", name, value) #     return super().__setattr__(name, value) # def __getattribute__(self, name): #     print("getting attribute", name) #     return super().__getattribute__(name) # a.mul(2, 3) # a.mul(0, 100) # a.mul(2, 100) # a.mul(2323, 3) # a.mul(2, 2) # a.mul(2, 3414) # another setter # print(vars(a)) # # settter # a.x = 1000 # # setter # a.x = "23931903" # # finally a getter # my_getter_val = a.x