Skip to content

@runtime_checkable not working #149115

@codingpwlorsk

Description

@codingpwlorsk

Bug report

Bug description:

from __future__ import annotations
from functools import partial
from dataclasses import dataclass
from typing import overload
from typing import runtime_checkable
from typing import Protocol


_traits_registry = set()
_impl_registry: dict[str,  dict[str, "Trait"]] = {}
_base_trait = None
_base_impl = None


def _is_user_created_meth(name: str):
    return not (name.startswith("__") & name.endswith("__"))


class _TraitImplementation:
    def __give_self(self, func):
        def give_self(*args, **kwargs):
            return func(*((self.__instance,) + args), **kwargs)
        return give_self

    def __init__(self, trait, cls, instance):
        self.__instance = instance
        for i in list(filter(_is_user_created_meth, dir(trait))):
            setattr(self, i, self.__give_self(getattr(cls, i)))


def get_trait[T](trait: type[T], instance) -> T:
    return getattr(instance, f"_Trait_.{trait.__module__}.{trait.__qualname__}")(instance)


@dataclass
class TraitSkeleton:
    name: str
    module: str
    qualname: str


def convert_trait_to_skeloton[T](trait: type[T]) -> T:
    return TraitSkeleton(trait.__name__, trait.__module__, trait.__qualname__)


@overload
def has_trait(trait: "Trait", cls) -> bool:
    pass


@overload
def has_trait(trait: TraitSkeleton, cls) -> bool:
    pass


def has_trait(trait: TraitSkeleton | "Triat", cls) -> bool:
    if not hasattr(cls, "_Trait_trait"):
        return False
    if isinstance(trait, TraitSkeleton):
        skeleton = trait
    else:
        skeleton = convert_trait_to_skeloton(trait)
    if not skeleton.module in cls._Trait_trait:
        return False
    if not skeleton.qualname in cls._Trait_trait[skeleton.module]:
        return False
    return True


class ImplMeta(type):
    def __new__(mcs, name, bases, namespace):
        global _base_impl
        cls = super().__new__(mcs, name, bases, namespace)
        is_base_impl = bases == ()
        if is_base_impl:
            if _base_impl is not None:
                raise TypeError(f"only one base Impl can exsist. deleate {name}")
            _base_impl = cls
            return cls
        inher = bases[0]
        if inher is _base_impl:
            _impl_registry[cls.__module__] = {cls.__qualname__ : cls}
        return cls


class TraitMeta(type):
    def __new__(mcs, name, bases, namespace):
        global _base_trait
        global _impl_registry
        global _traits_registry
        cls = super().__new__(mcs, name, bases, namespace)
        is_trait_cls = bases == ()
        if is_trait_cls:
            if _base_trait is not None:
                raise TypeError("only one base trait can exsist")
            _base_trait = cls
            return cls
        base = bases[0]
        if base is _base_trait:
            _traits_registry.add(cls)
            return cls
        elif base in _traits_registry:
            non_dunder_meth_trait = list(filter(_is_user_created_meth, dir(base)))
            non_dunder_meth_cls = list(filter(_is_user_created_meth, dir(cls)))
            for method in non_dunder_meth_trait:
                if not method in namespace:
                    raise TypeError(f"method {method} not in {name}, within the {base.__name__} trait")
                # else:
                #     name_space_sig = signature(namespace[method])
                #     trait_meth_sig = signature(getattr(base, method))
                #     implement_rule = False
            try:
                modified_class = _impl_registry[cls.__module__][cls.__qualname__]
            except KeyError:
                raise TypeError(f"class {name} is not registerd.")
            _Trait_impl_layout = partial(_TraitImplementation, base, cls)
            setattr(modified_class, f"_Trait_.{base.__module__}.{base.__qualname__}", _Trait_impl_layout)
            _impl_registry[modified_class.__module__][modified_class.__qualname__] = modified_class
            return modified_class


class RunTimeMeta(type):
    def __new__(mcs, name, bases, namespace):
        return super().__new__(mcs, name, bases, namespace)


    def __getitem__(self, *traits: list[type[Trait]]):
        def dummy_func():
            pass
        name = "_RunTimeMeta_"
        methods = {}
        for trait in traits:
            methods[f"_trait_.{trait.__module__}.{trait.__qualname__}"] = dummy_func
            name += f".{trait.__module__}.{trait.__qualname__}"
        working_cls = type(name, (Protocol,), methods)
        return runtime_checkable(working_cls)


class RunTime(metaclass=RunTimeMeta):
    pass


class Impl(metaclass=ImplMeta):
    pass


class Trait(metaclass=TraitMeta):
    pass


# class Rule(Trait):
#     def Rule(self, trait_func_sig: Signature, impl_func_sig: Signature) -> bool:
#         raise TypeError("This is ment to be overwritten")


class Cold(Trait):
    def is_cold(self) -> bool:
        pass
 

class Cop(Impl):
    def __init__(self, social_temp: int, phisical_temp: int):
        self.social_temp = social_temp
        self.phisical_temp = phisical_temp


class Cop(Cold):
    def is_cold(self):
        pass


get_trait(Cold, Cop(0,0))


@runtime_checkable
class runtime(Protocol):
    def plswork(self):
        pass


def social_cold(plswork: runtime):
    print("yippy")

dummy_cop = Cop(100, 100)
social_cold(dummy_cop)
social_cold("pls dont work")

### CPython versions tested on:

3.14

### Operating systems tested on:

_No response_

Metadata

Metadata

Assignees

No one assigned

    Labels

    pendingThe issue will be closed if no feedback is providedtopic-typing

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions