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