Source code for jgdv.decorators.mixin

  1
  2#!/usr/bin/env python3
  3"""
  4
  5"""
  6# Imports:
  7from __future__ import annotations
  8
  9# ##-- stdlib imports
 10import datetime
 11import enum
 12import functools as ftz
 13import itertools as itz
 14import logging as logmod
 15import pathlib as pl
 16import re
 17import time
 18import types
 19import typing
 20import weakref
 21from uuid import UUID, uuid1
 22
 23# ##-- end stdlib imports
 24
 25from jgdv.debugging import TraceBuilder
 26from ._core import MonotonicDec, IdempotentDec, Decorator, DataDec
 27from jgdv.mixins.annotate import Subclasser
 28
 29# ##-- types
 30# isort: off
 31import abc
 32import collections.abc
 33from typing import TYPE_CHECKING, cast, assert_type, assert_never
 34from typing import Generic, NewType, TypeAliasType
 35# Protocols:
 36from typing import Protocol, runtime_checkable
 37# Typing Decorators:
 38from typing import no_type_check, final, override, overload
 39from types import resolve_bases, FunctionType, MethodType
 40
 41if TYPE_CHECKING:
 42    from ._interface import Decorated
 43    from ._interface import Decorable, DForm_e
 44    from jgdv import Maybe
 45    from typing import Final
 46    from typing import ClassVar, Any, LiteralString
 47    from typing import Never, Self, Literal
 48    from typing import TypeGuard
 49    from collections.abc import Iterable, Iterator, Callable, Generator
 50    from collections.abc import Sequence, Mapping, MutableMapping, Hashable
 51
 52# isort: on
 53# ##-- end types
 54
 55##-- logging
 56logging = logmod.getLogger(__name__)
 57##-- end logging
 58
 59##--| Global Vars:
 60MIXIN_KWD : Final[str] = "_jgdv_mixins"
 61ABSMETHS  : Final[str] = "__abstractmethods__"
 62IS_ABS    : Final[str] = "__isabstractmethod__"
 63NAME_MOD  : Final[str] = "M"
 64##--| Funcs
 65
 66##--| Body
 67
[docs] 68class Mixin(MonotonicDec): 69 """ Decorator to App/Prepend Mixins into the decorated class. 70 71 Converts:: 72 73 class ClsName(mixins, Supers, Protocols, metaclass=MCls, **kwargs):... 74 75 into:: 76 77 @Protocols(*ps) 78 @Mixin(*ms, None) 79 class ClsName(Supers): ... 80 81 82 ('None' is used to separate pre and post mixins) 83""" 84 85 __builder : ClassVar[Subclasser] = Subclasser() 86 needs_args = True 87 88 def __init__(self, *mixins:Maybe[type], allow_inheritance:bool=False, silent:bool=False) -> None: 89 super().__init__() 90 self._silent = silent 91 self._name_mod = NAME_MOD 92 try: 93 index = mixins.index(None) 94 except ValueError: 95 index = 0 96 finally: 97 self._pre_mixins = [x for x in mixins[:index] if x is not None and x is not object] 98 self._post_mixins = [x for x in mixins[index:] if x is not None and x is not object] 99 if not allow_inheritance: 100 self._validate_mixins() 101 102
[docs] 103 def _validate_mixins(self) -> None: 104 for x in itz.chain(self._pre_mixins, self._post_mixins): 105 if len(x.mro()) > 3: # noqa: PLR2004 106 msg = "Can't Mixin a class that inherits anything" 107 raise TypeError(msg, x)
108
[docs] 109 def _build_annotations_h(self, target:Decorable, current:list) -> Maybe[list]: # noqa: ARG002 110 """ Given a list of the current annotation list, 111 return its replacement 112 """ 113 new_mixins = current[:] 114 new_mixins = [x for x in self._pre_mixins if x not in current] 115 new_mixins += [x for x in self._post_mixins if x not in current] 116 return new_mixins
117
[docs] 118 def _validate_target_h(self, target:Decorable, form:DForm_e, args:Maybe[list]=None) -> None: # noqa: ARG002 119 match target: 120 case type() if hasattr(target, "model_fields"): 121 msg = "Pydantic classes shouldn't be extended" 122 raise TypeError(msg, target) 123 case type(): 124 pass 125 case _: 126 msg = "Unexpected type passed for mixin annotation" 127 raise TypeError(msg, target)
128
[docs] 129 def _wrap_class_h(self, cls:Decorable) -> Decorated: 130 assert(isinstance(cls, type)) 131 match cls.mro(): 132 case [x, *xs]: 133 new_mro = [*self._pre_mixins, x, *self._post_mixins, *xs] 134 case _: 135 pass 136 match self._silent: 137 case False: 138 new_name = self.__builder.decorate_name(cls, self._name_mod) 139 case True: 140 new_name = cls.__name__ 141 142 mixed = self.__builder.make_subclass(new_name, cls, mro=new_mro) 143 self.annotate_decorable(mixed) 144 return mixed
145 146
[docs] 147class DelayMixin(DataDec): 148 """ TODO A Decorator for annotating a class with mixins 149 150 Delays the construction of the True class until later, 151 using @MixinNow 152 """ 153 pass
154
[docs] 155class MixinNow(MonotonicDec): 156 """ TODO The trigger for delayed mixins. 157 158 After using @DelayMixin, 159 trigger the True class using this. 160 161 eg:: 162 163 @MixinNow 164 @DelayMixin(m3, None, m4) 165 @DelayMixin(m1, m2) 166 class Blah:... 167 168 """ 169 pass