@@ -80,6 +80,7 @@ def execute(self):
80
80
"_ntsubtable" ,
81
81
"_ntwritedefault" ,
82
82
# "__doc__",
83
+ "__orig_class__" ,
83
84
"_topic_type" ,
84
85
"_nt" ,
85
86
)
@@ -100,13 +101,47 @@ def __init__(
100
101
self ._ntwritedefault = writeDefault
101
102
# self.__doc__ = doc
102
103
103
- self ._topic_type = _get_topic_type_for_value (self ._ntdefault )
104
- if self ._topic_type is None :
105
- checked_type : type = type (self ._ntdefault )
104
+ # Defer checks for empty sequences to check type hints.
105
+ # Report errors here when we can so the error points to the tunable line.
106
+ if default or not isinstance (default , collections .abc .Sequence ):
107
+ self ._topic_type = _get_topic_type_for_value (default )
108
+ if self ._topic_type is None :
109
+ checked_type : type = type (default )
110
+ raise TypeError (
111
+ f"tunable is not publishable to NetworkTables, type: { checked_type .__name__ } "
112
+ )
113
+
114
+ def __set_name__ (self , owner : type , name : str ) -> None :
115
+ type_hint : Optional [type ] = None
116
+ # __orig_class__ is set after __init__, check it here.
117
+ orig_class = getattr (self , "__orig_class__" , None )
118
+ if orig_class is not None :
119
+ # Accept field = tunable[Sequence[int]]([])
120
+ type_hint = typing .get_args (orig_class )[0 ]
121
+ else :
122
+ type_hint = typing .get_type_hints (owner ).get (name )
123
+ origin = typing .get_origin (type_hint )
124
+ if origin is typing .ClassVar :
125
+ # Accept field: ClassVar[tunable[Sequence[int]]] = tunable([])
126
+ type_hint = typing .get_args (type_hint )[0 ]
127
+ origin = typing .get_origin (type_hint )
128
+ if origin is tunable :
129
+ # Accept field: tunable[Sequence[int]] = tunable([])
130
+ type_hint = typing .get_args (type_hint )[0 ]
131
+
132
+ if type_hint is not None :
133
+ topic_type = _get_topic_type (type_hint )
134
+ else :
135
+ topic_type = _get_topic_type_for_value (self ._ntdefault )
136
+
137
+ if topic_type is None :
138
+ checked_type : type = type_hint or type (self ._ntdefault )
106
139
raise TypeError (
107
140
f"tunable is not publishable to NetworkTables, type: { checked_type .__name__ } "
108
141
)
109
142
143
+ self ._topic_type = topic_type
144
+
110
145
@overload
111
146
def __get__ (self , instance : None , owner = None ) -> "tunable[V]" : ...
112
147
@@ -218,7 +253,7 @@ class MyComponent:
218
253
navx: ...
219
254
220
255
@feedback
221
- def get_angle(self):
256
+ def get_angle(self) -> float :
222
257
return self.navx.getYaw()
223
258
224
259
class MyRobot(magicbot.MagicRobot):
@@ -297,6 +332,8 @@ def _get_topic_type(
297
332
if hasattr (inner_type , "WPIStruct" ):
298
333
return lambda topic : ntcore .StructArrayTopic (topic , inner_type )
299
334
335
+ return None
336
+
300
337
301
338
def collect_feedbacks (component , cname : str , prefix : Optional [str ] = "components" ):
302
339
"""
0 commit comments