#     Copyright 2026, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file


from __future__ import print_function

import inspect
import pprint


def displayDir(l):
    if "__firstlineno__" in l:
        l.remove("__firstlineno__")

    if "__static_attributes__" in l:
        l.remove("__static_attributes__")

    return pprint.pformat(l)


def displayDict(d):
    if "__loader__" in d:
        d = dict(d)
        d["__loader__"] = "<__loader__ removed>"

    if "__file__" in d:
        d = dict(d)
        d["__file__"] = "<__file__ removed>"

    if "__firstlineno__" in d:
        d = dict(d)
        del d["__firstlineno__"]

    # Avoid recursion that we don't offer for classes.
    if "__locals__" in d:
        d = dict(d)
        del d["__locals__"]

    return pprint.pformat(d)


class SimpleClass:
    "The class documentation."  # Leading space on purpose.

    # TODO: Doesn't work with Python3, because we don't yet make our own dict
    # visible.  print locals()
    print("Class locals, while building", displayDict(locals()))

    class_var = 1

    def __init__(self, init_parameter):
        self.x = init_parameter

    def normal_method(self, arg1, arg2):
        self.arg1 = arg1
        self.arg2 = arg2

        return self.arg1, self.arg2, self.x, self

    @staticmethod
    def static_method():
        return "something"


print("Simple class:", SimpleClass)
print("Lives in", SimpleClass.__module__)
print("Documentation", SimpleClass.__doc__)
print("Instantiate simple class:", SimpleClass(14))
print("Call simple class normal method:", SimpleClass(11).normal_method(1, 2))
print("Call simple class static method:", SimpleClass(11).static_method())


class MetaClass(type):
    def __init__(cls, name, bases, dictionary):
        print("MetaClass is called.")
        cls.added_in = 5


print(MetaClass)


class ComplexClass:
    __metaclass__ = MetaClass


print(ComplexClass, displayDir(dir(ComplexClass)))

print(ComplexClass, hasattr(ComplexClass, "added_in") and ComplexClass.added_in)


def function():
    x = 1

    class DynamicClass:
        y = x

    x = 2

    return DynamicClass


print(function(), function().y)


def strangeClassBehavior():
    class StrangeClass(object):
        count = 0

        def __new__(cls):
            print("__new__")

            cls.count += 1
            return object.__new__(cls)

        def __del__(self):
            print("__del__")

            cls = self.__class__
            cls.count -= 1
            assert cls.count >= 0

    x = StrangeClass()

    return x.count


print("Strange class with __new__ and __del__ overloads", strangeClassBehavior())


class ClosureLocalizer:
    function = function

    # Using what looks like a method as a decorator.
    def deco(f):
        f.decorated = True

        return f

    @deco
    def x(self):
        pass


print("Class with a name from module level renamed to local", ClosureLocalizer.function)
print("Class method decorated", ClosureLocalizer().x.decorated)


class MethodDispatcher(object):
    def __init__(self, name):
        self.name = name
        self.types = []

    def add(self, types, func):
        self.types.append(types)


def dispatch(*types):
    def _decorator(func):
        name = func.__name__
        class_locals = inspect.currentframe().f_back.f_locals
        dispatcher = class_locals.get(name, MethodDispatcher(name))
        dispatcher.add(types, func)
        return dispatcher

    return _decorator


class A(object):
    pass


class B(object):
    pass


class FrameLocalsDecoratorClass(object):
    @dispatch(A)
    def run(self, value):
        return "A"

    @dispatch(B)
    def run(self, value):
        return "B"


print(
    "Class frame locals decorators",
    [types[0].__name__ for types in FrameLocalsDecoratorClass.run.types],
)

print("Class with decorator and meta class:")


def class_decorator(cls):
    try:
        print("cls decorator", cls.added_in)
    except AttributeError:
        print("cls decorator has not added")

    return cls


@class_decorator
class MyClass:
    __metaclass__ = MetaClass


print("Class that updates its locals:")


class DictUpdating:
    a = 1

    locals().update({"b": 2})

    for f in range(6):
        locals()["test_%s" % f] = f


print("Changed values", DictUpdating.b, DictUpdating.test_4)


def functionThatOffersClosureToPassThroughClass(x):
    class Foo:
        global x
        x = 1

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

    return Foo()


print(functionThatOffersClosureToPassThroughClass(6)(2), x)


class NameCollisionClosure:
    def x(self):
        return x


print(NameCollisionClosure, NameCollisionClosure().x())


class ClassesWithNestedClass:
    class NestedClass(object):
        def getDict(self):
            return {"a": 2}


print(
    "Classes:",
    ClassesWithNestedClass,
    ClassesWithNestedClass().NestedClass,
    ClassesWithNestedClass().NestedClass().getDict(),
)

secondary = "global closure wins"


class ClassWithModuleVariableCollisionMain:
    secondary = None

    def __init__(self):
        self.secondary = self.Child()
        self.value = self.secondary.attr

    class Child:
        def __init__(self):
            self.attr = secondary


print(
    ClassWithModuleVariableCollisionMain, ClassWithModuleVariableCollisionMain().value
)

#     Python tests originally created or extracted from other peoples work. The
#     parts were too small to be protected.
#
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.
