1- from typing import get_args , get_origin , Iterable , Union , Literal , Any , TYPE_CHECKING , TypeVar
2- from abc import ABC
1+ from typing import get_args , get_origin , Iterable , Union , Literal , Any , TYPE_CHECKING , TypeVar , Generic
32from inspect import isabstract
43from contextlib import suppress
4+ from itertools import chain
5+ from functools import cache
56
67from ..convert import *
78from ..aliasing import *
@@ -92,31 +93,30 @@ def __init__(
9293 self .init_main_frame ()
9394
9495 @staticmethod
95- def get_cls_name (cls : T ) -> Union [str , T ]:
96+ @cache
97+ def get_cls_name (cls : Any , args : bool = False ) -> str :
9698 """
9799 Returns the name of the class ``cls`` or
98100 the original class when the name cannot be obtained.
99101 If alias exists, alias is returned instead.
100102 """
101- name = NewObjectFrameBase ._get_cls_unaliased_name (cls )
102103 if (alias := get_aliased_name (cls )) is not None :
103104 return alias + f" ({ name } )"
105+ elif hasattr (cls , "__name__" ):
106+ name = cls .__name__
107+ elif hasattr (cls , "_name" ) and cls ._name :
108+ name = cls ._name
109+ else :
110+ name = str (cls )
111+
112+ if args and (type_args := get_args (cls )):
113+ name = (
114+ name +
115+ f"[{ ', ' .join ([NewObjectFrameBase .get_cls_name (x ) for x in type_args ]) } ]"
116+ )
104117
105118 return name
106119
107- @staticmethod
108- def _get_cls_unaliased_name (cls : T ) -> Union [str , T ]:
109- """
110- Returns the name of the class ``cls`` or
111- the original class when the name cannot be obtained.
112- """
113- if hasattr (cls , "__name__" ):
114- return cls .__name__
115- if hasattr (cls , "_name" ):
116- return cls ._name
117-
118- return cls
119-
120120 @classmethod
121121 def set_origin_window (cls , window : "ObjectEditWindow" ):
122122 cls .origin_window = window
@@ -143,7 +143,7 @@ def cast_type(cls, value: Any, types: Iterable):
143143
144144 return value
145145
146- for type_ in filter (lambda t : cls . _get_cls_unaliased_name ( t ) in __builtins__ , types ):
146+ for type_ in filter (lambda t : t . __module__ == "builtins" , types ):
147147 with suppress (Exception ):
148148 cast_funct = CAST_FUNTIONS .get (type_ )
149149 if cast_funct is None :
@@ -157,7 +157,7 @@ def cast_type(cls, value: Any, types: Iterable):
157157 return value
158158
159159 @classmethod
160- def convert_types (cls , types_in ):
160+ def convert_types (cls , input_type : type ):
161161 """
162162 Type preprocessing method, that extends the list of types with inherited members (polymorphism)
163163 and removes classes that are wrapped by some other class, if the wrapper class also appears in
@@ -177,33 +177,36 @@ def remove_classes(types: list):
177177
178178 return tuple (r )
179179
180- if get_origin (types_in ) is Union :
181- types_in = cls .convert_types (get_args (types_in ))
180+ origin = get_origin (input_type )
181+ # Unpack Union items into a tuple
182+ if origin is Union or issubclass_noexcept (origin , Iterable ):
183+ new_types = dict ()
184+ for type_ in chain .from_iterable ([cls .convert_types (r ) for r in get_args (input_type )]):
185+ new_types [type_ ] = 0 # Use dictionary's keys as OrderedSet, with dummy value 0
182186
183- elif issubclass_noexcept (origin := get_origin (types_in ), Iterable ) and types_in is not str :
184- types_in = origin [cls .convert_types (get_args (types_in ))]
187+ new_types = tuple (new_types )
188+ if origin is Union :
189+ return new_types
185190
186- if isinstance (types_in , tuple ):
187- new_types = []
188- for t in types_in :
189- new_types .extend (cls .convert_types (t ))
191+ return (origin [new_types ],)
190192
191- types_in = new_types
192- else :
193- types_in = ( types_in ,)
193+ if issubclass_noexcept ( origin , Generic ):
194+ # Patch for Python versions < 3.10
195+ input_type . __name__ = origin . __name__
194196
195- # Also include inherited objects
196- subtypes = []
197- for t in types_in :
198- if cls .get_cls_name (t ) in __builtins__ :
199- continue # Don't consider built-int types for polymorphism
197+ if input_type .__module__ == "builtins" :
198+ # Don't consider built-int types for polymorphism
199+ # No removal of abstract classes is needed either as builtins types aren't abstract
200+ return (input_type ,)
200201
201- if hasattr (t , "__subclasses__" ):
202- for st in t .__subclasses__ ():
203- subtypes .extend (cls .convert_types (st ))
202+ # Extend subclasses
203+ subtypes = []
204+ if hasattr (input_type , "__subclasses__" ):
205+ for st in input_type .__subclasses__ ():
206+ subtypes .extend (cls .convert_types (st ))
204207
205208 # Remove wrapped classes (eg. wrapped by decorator) + ABC classes
206- return remove_classes ([* types_in , * subtypes ])
209+ return remove_classes ([input_type , * subtypes ])
207210
208211 def init_main_frame (self ):
209212 frame_main = ttk .Frame (self )
0 commit comments