@@ -13,9 +13,13 @@ class BaseStyle:
13
13
"""A base class for style declarations.
14
14
15
15
Exposes a dict-like interface. Designed for subclasses to be decorated
16
- with @dataclass(kw_only=True), which most IDEs should be able to interpret and
17
- provide autocompletion of argument names. On Python < 3.10, init=False can be used
18
- to still get the keyword-only behavior from the included __init__.
16
+ with @dataclass(kw_only=True, repr=False).
17
+
18
+ The kw_only parameter was added in Python 3.10; for 3.9, init=False can be used
19
+ instead to still get the keyword-only behavior from the included __init__.
20
+
21
+ Most IDEs should see the dataclass decorator and provide autocompletion / type hints
22
+ for parameters to the constructor.
19
23
"""
20
24
21
25
_BASE_PROPERTIES = defaultdict (set )
@@ -26,10 +30,35 @@ def __init_subclass__(cls):
26
30
cls ._PROPERTIES = cls ._BASE_PROPERTIES [cls ]
27
31
cls ._ALL_PROPERTIES = cls ._BASE_ALL_PROPERTIES [cls ]
28
32
33
+ ########################################################################
34
+ # 03-2025: Backwards compatibility for Toga < 0.5.0 *and* for Python 3.9
35
+ ########################################################################
36
+
29
37
# Fallback in case subclass isn't decorated as dataclass (probably from using
30
38
# previous API) or for pre-3.10, before kw_only argument existed.
31
39
def __init__ (self , ** properties ):
32
- self .update (** properties )
40
+ try :
41
+ self .update (** properties )
42
+ except NameError :
43
+ # It still makes sense for update() to raise a NameError. However, here we
44
+ # simulate the behavior of the dataclass-generated __init__() for
45
+ # consistency.
46
+ for name in properties :
47
+ # This is redoing work, but it should only ever happen when a property
48
+ # name is invalid, and only in outdated Python or Toga, and only once.
49
+ if name not in self ._ALL_PROPERTIES :
50
+ raise TypeError (
51
+ f"{ type (self ).__name__ } .__init__() got an unexpected keyword "
52
+ f"argument '{ name } '"
53
+ )
54
+ # The above for loop should never run to completion, so that needs to be
55
+ # excluded from coverage.
56
+ else : # pragma: no cover
57
+ pass
58
+
59
+ ######################################################################
60
+ # End backwards compatibility
61
+ ######################################################################
33
62
34
63
@property
35
64
def _applicator (self ):
@@ -197,6 +226,12 @@ def __str__(self):
197
226
f"{ name .replace ('_' , '-' )} : { value } " for name , value in sorted (self .items ())
198
227
)
199
228
229
+ def __repr__ (self ):
230
+ properties = ", " .join (
231
+ f"{ name } ={ repr (value )} " for name , value in sorted (self .items ())
232
+ )
233
+ return f"{ type (self ).__name__ } ({ properties } )"
234
+
200
235
######################################################################
201
236
# Backwards compatibility
202
237
######################################################################
0 commit comments