Decorators for support for developers tool within function

The reason for this is because, if a programmer implemented a
uniqe feture, such as this

from something import *

class Cop(Impl):
  pass

class Cop(Job):
  pass

class Cop(Gender):
  pass

Mypy, Ruff8, and other tools would thrown a waring, even though it was entirely intended.
One solution to this would a built-in module, providing methods that would allow for developers, to tell Mypy, Flake8, and Ruff8, to ignore something. For exsample,

import dev_tool
from something import *

class Cop(Impl):
  pass

@dev_tool.ignore(dev_tool.REDEFINE)
class Cop(Job):
  pass

@dev_tool.ignore(dev_tool.REDEFINE)
class Cop(Gender):
  pass

Overall, I believe python should provide a built-in module that allows for
developers to tell tools that certen things are intended

1 Like

There’s @typing.no_type_check (docs).

what about for redefining classes & functions?

also [no-redef] is an warning in mypy

I don’t understand what’s intended here. How will you use these classes?

5 Likes

That’s exactly what @typing.no_type_check does

Mypy understands @typing.no_type_check

it still gives a warning.
copy this entire code and you should see that it still gives a warning.

import typing

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

@typing.no_type_check
class Cop(SocialyCold):
    def is_cold(self):
        pass


@typing.no_type_check
class Cop(PhisicalyCold):
    def is_cold(self):
        pass

Well, yea; that’s to be expected for invalid Python code like this.

But if you’re talking about mypy still reporting no-redef in spite of the @typing.no_type_check decorator, then I guess you could argue that this should be changed in mypy.

Either way, your example code is incomplete, unclear, and rather unconventional, and you haven’t proposed anything concrete, and did not elaborate even after being explicitly asked to do so. Please be more mindful of our time, and put more effort into describing what exactly it is that you want here, instead of vaguely dismissing our attempts at helping you.

6 Likes

Maybe the base classes have some metaprogramming to capture subclasses, but I’m grasping

1 Like

True! Sorry for not mentioning it
this is the entire implementation of it

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
from typing import Any


_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 isinstance(trait, TraitSkeleton):
        skeleton = trait
    else:
        skeleton = convert_trait_to_skeloton(triat)
    return hasattr(trait, f"_Triat_.{skeleton.module}.{skeleton.name}")


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():
            raise TypeError("You are not suppose to run this")
        name = "_RunTimeMeta_"
        methods = {}
        for trait in traits[0]:
            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


def runtime_type_check_traits(instance: Any, traits: RunTime):
    okay =  isinstance(instance, traits)
    if not okay:
        def proper_meth(name: str):
            return name.startswith("_Trait_")
        def format_meth(name: str):
            if name[0:17] == "_Trait_.builtins.":
                return name.lstrip("_Triat_.builtins.")
            return name.lstrip("_Trait_.")
        failed_traits = list(filter(proper_meth, dir(traits)))
        failed_traits = list(map(format_meth, failed_traits))
        raise TypeError(f"{instance} deos not have one of requird traits {failed_traits}")

Any classes that implement Impl is meant to be redefine in some way. So when
Mypy gives a warning for intended behavior, it gets annoying

In my experience, this usually either means that I need to disable a linter rule because it doesn’t suit my project, or I need to rethink the way I’ve implemented something.

2 Likes

Sorry, I’m currently implementing a metaclass. When a class implements Impl, it is meant to be redefine later in a program, but when someone redefine a class, it gives a warning, even though it is intended. So I’m currently trying to find a solution to this problem, of unintended warnings.

This feels like an odd design choice, and I’m not surprised to hear that standard checkers flag it as wrong. I’m not saying that there isn’t a reason you need to design things the way that you have, but at some point you’re so far from “normal” coding patterns that you probably just have to accept that standard linters and type checkers won’t be happy with your code…

7 Likes

True, I just wish we have decorators for linters and type checkers like typing.no_type_check that tells these tools that a certain behavior is intended, even how obscure it is.

I think you should redesign this. It’s extremely convoluted and I think poor design.

In Python, you should generally avoid metaclasses. They are hard to combine, and they’re hard to reason about.

Also, it’s better to build the registries explicitly rather than having them constructed magically on definition. Yes, I see that you’re trying to follow DRY, but you made your code really hard to understand.

I would just define your triplet, and add them to a global registry explicitly:

register(MyRuntime, MyImpl, MyTrait)

Yes, it’s one extra line per triplet, but it will be much more understandable and avoids metaclasses and redefinition.

2 Likes