Skip to content

Commit f206e53

Browse files
committed
Fixed typing system
1 parent 923a483 commit f206e53

File tree

6 files changed

+75
-44
lines changed

6 files changed

+75
-44
lines changed

docs/source/changelog.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ Glossary
2424
Releases
2525
---------------------
2626

27+
v1.2.4
28+
================
29+
- Fixed typing system. (names of types)
30+
31+
2732
v1.2.3
2833
================
2934
- Fixed annotations not getting obtained for function definitions.

tkclasswiz/__init__.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,33 @@
11
"""
22
TkClassWizard - Library for graphically defining objects based on class annotations.
33
Works with Tkinter / TTKBootstrap.
4+
5+
-------------------
6+
7+
MIT License
8+
9+
Copyright (c) 2023 David Hozic
10+
11+
Permission is hereby granted, free of charge, to any person obtaining a copy
12+
of this software and associated documentation files (the "Software"), to deal
13+
in the Software without restriction, including without limitation the rights
14+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15+
copies of the Software, and to permit persons to whom the Software is
16+
furnished to do so, subject to the following conditions:
17+
18+
The above copyright notice and this permission notice shall be included in all
19+
copies or substantial portions of the Software.
20+
21+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27+
SOFTWARE.
428
"""
529

6-
__version__ = "1.2.3"
30+
__version__ = "1.2.4"
731

832
from .object_frame import *
933
from .annotations import *

tkclasswiz/convert.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""
22
Modules contains definitions related to GUI object transformations.
33
"""
4-
54
from typing import Any, Union, List, Generic, TypeVar, Mapping, Optional
65
from contextlib import suppress
76
from inspect import signature

tkclasswiz/object_frame/frame_base.py

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
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
32
from inspect import isabstract
43
from contextlib import suppress
4+
from itertools import chain
5+
from functools import cache
56

67
from ..convert import *
78
from ..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)

tkclasswiz/object_frame/frame_iterable.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ def __init__(
8888
ttk.Button(frame_up_down, text="Up", command=lambda: w.move_selection(-1)).pack(side="left", fill=tk.X, expand=True)
8989
ttk.Button(frame_up_down, text="Down", command=lambda: w.move_selection(1)).pack(side="left", fill=tk.X, expand=True)
9090

91-
for arg in get_args(self.class_):
92-
menu.add_command(label=self.get_cls_name(arg), command=partial(self.new_object_frame, arg, w))
91+
for arg in get_args(self.convert_types(class_)[0]):
92+
menu.add_command(label=self.get_cls_name(arg, True), command=partial(self.new_object_frame, arg, w))
9393

9494
w.pack(side="left", fill=tk.BOTH, expand=True)
9595

tkclasswiz/object_frame/frame_struct.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def fill_values(k: str, entry_types: list, menu: tk.Menu, combo: ComboBoxObjects
155155
any_filled = True
156156
if self.allow_save:
157157
menu.add_command(
158-
label=f"New {self.get_cls_name(entry_type)}",
158+
label=f"New {self.get_cls_name(entry_type, args=True)}",
159159
command=partial(self.new_object_frame, entry_type, combo)
160160
)
161161

0 commit comments

Comments
 (0)