# # Patch
#
# Literate notebooks benefit from splitting their code and documentation across several cells. Unfortunately, the nature of the notebook-kernel execution model introduces some constraints upon this, as it is impossible to extend Python local namespaces across different cells. To facilitate this, we introduce the `patch` decorator which operates at runtime and build time to unify separate definitions.

# %load_ext literary.module

from typing import Callable, Type, TypeVar

T = TypeVar("T")

# Some wrapper classes store the original object in a named attribute. Here we define a few of the common cases.

WRAPPER_NAMES = "fget", "fset", "fdel", "__func__", "func"


# Let's implement the *runtime* decorator, which monkeypatches the class with the decorated function

def patch(cls: Type) -> Callable[[T], T]:
    """Decorator to monkey-patch additional methods to a class.

    At import-time, this will disappear and the source code itself will be transformed
    Inside notebooks, the implementation below will be used.

    :param cls:
    :return:
    """

    def get_name(func):
        # Fix #4 to support patching (property) descriptors
        try:
            return func.__name__
        except AttributeError:
            # Support various descriptors
            for attr in WRAPPER_NAMES:
                try:
                    return getattr(func, attr).__name__
                except AttributeError:
                    continue

            # Raise original exception
            raise

    def _notebook_patch_impl(func):
        setattr(cls, get_name(func), func)
        return func

    return _notebook_patch_impl


# We can now implement a test class to see this decorator in action

class TestClass:
    pass


# At runtime, an instantiated class can have new methods attached to its type

obj = TestClass()


@patch(TestClass)
def method_a(self):
    return "method a"


# And we can see that the method behaves as expected

assert obj.method_a() == "method a"

StarcoderdataJupyterScriptsDedupFiltered