16
16
from matplotlib .artist import Artist as _Artist
17
17
18
18
from data_prototype .containers import DataContainer , _MatplotlibTransform
19
+ from data_prototype .conversion_node import (
20
+ ConversionNode ,
21
+ RenameConversionNode ,
22
+ evaluate_pipeline ,
23
+ FunctionConversionNode ,
24
+ LimitKeysConversionNode ,
25
+ )
19
26
20
27
21
28
class _BBox (Protocol ):
@@ -139,45 +146,26 @@ def _query_and_transform(self, renderer, *, xunits: List[str], yunits: List[str]
139
146
return self ._cache [cache_key ]
140
147
except KeyError :
141
148
...
142
- # TODO decide if units go pre-nu or post-nu?
143
- for x_like in xunits :
144
- if x_like in data :
145
- data [x_like ] = ax .xaxis .convert_units (data [x_like ])
146
- for y_like in yunits :
147
- if y_like in data :
148
- data [y_like ] = ax .xaxis .convert_units (data [y_like ])
149
-
150
- # doing the nu work here is nice because we can write it once, but we
151
- # really want to push this computation down a layer
152
- # TODO sort out how this interoperates with the transform stack
153
- transformed_data = {}
154
- for k , (nu , sig ) in self ._sigs .items ():
155
- to_pass = set (sig .parameters )
156
- transformed_data [k ] = nu (** {k : data [k ] for k in to_pass })
149
+ # TODO units
150
+ transformed_data = evaluate_pipeline (self ._converters , data )
157
151
158
152
self ._cache [cache_key ] = transformed_data
159
153
return transformed_data
160
154
161
- def __init__ (self , data , nus , ** kwargs ):
155
+ def __init__ (self , data , converters : ConversionNode | list [ ConversionNode ] | None , ** kwargs ):
162
156
super ().__init__ (** kwargs )
163
157
self .data = data
164
158
self ._cache = LFUCache (64 )
165
159
# TODO make sure mutating this will invalidate the cache!
166
- self ._nus = nus or {}
167
- for k in self .required_keys :
168
- self ._nus .setdefault (k , _make_identity (k ))
169
- desc = data .describe ()
170
- for k in self .expected_keys :
171
- if k in desc :
172
- self ._nus .setdefault (k , _make_identity (k ))
173
- self ._sigs = {k : (nu , inspect .signature (nu )) for k , nu in self ._nus .items ()}
160
+ if isinstance (converters , ConversionNode ):
161
+ converters = [converters ]
162
+ self ._converters : list [ConversionNode ] = converters or []
163
+ setters = list (self .expected_keys | self .required_keys )
164
+ if hasattr (self , "_wrapped_class" ):
165
+ setters += [f [4 :] for f in dir (self ._wrapped_class ) if f .startswith ("set_" )]
166
+ self ._converters .append (LimitKeysConversionNode .from_keys (setters ))
174
167
self .stale = True
175
168
176
- # TODO add a setter
177
- @property
178
- def nus (self ):
179
- return dict (self ._nus )
180
-
181
169
182
170
class ProxyWrapper (ProxyWrapperBase ):
183
171
_privtized_methods : Tuple [str , ...] = ()
@@ -192,7 +180,7 @@ def __getattr__(self, key):
192
180
return getattr (self ._wrapped_instance , key )
193
181
194
182
def __setattr__ (self , key , value ):
195
- if key in ("_wrapped_instance" , "data" , "_cache" , "_nus " , "stale" , "_sigs" ):
183
+ if key in ("_wrapped_instance" , "data" , "_cache" , "_converters " , "stale" , "_sigs" ):
196
184
super ().__setattr__ (key , value )
197
185
elif hasattr (self , "_wrapped_instance" ) and hasattr (self ._wrapped_instance , key ):
198
186
setattr (self ._wrapped_instance , key , value )
@@ -205,9 +193,12 @@ class LineWrapper(ProxyWrapper):
205
193
_privtized_methods = ("set_xdata" , "set_ydata" , "set_data" , "get_xdata" , "get_ydata" , "get_data" )
206
194
required_keys = {"x" , "y" }
207
195
208
- def __init__ (self , data : DataContainer , nus = None , / , ** kwargs ):
209
- super ().__init__ (data , nus )
196
+ def __init__ (self , data : DataContainer , converters = None , / , ** kwargs ):
197
+ super ().__init__ (data , converters )
210
198
self ._wrapped_instance = self ._wrapped_class (np .array ([]), np .array ([]), ** kwargs )
199
+ self ._converters .insert (- 1 , RenameConversionNode .from_mapping ({"x" : "xdata" , "y" : "ydata" }))
200
+ setters = [f [4 :] for f in dir (self ._wrapped_class ) if f .startswith ("set_" )]
201
+ self ._converters [- 1 ] = LimitKeysConversionNode .from_keys (setters )
211
202
212
203
@_stale_wrapper
213
204
def draw (self , renderer ):
@@ -218,7 +209,6 @@ def draw(self, renderer):
218
209
219
210
def _update_wrapped (self , data ):
220
211
for k , v in data .items ():
221
- k = {"x" : "xdata" , "y" : "ydata" }.get (k , k )
222
212
getattr (self ._wrapped_instance , f"set_{ k } " )(v )
223
213
224
214
@@ -238,8 +228,8 @@ class PathCollectionWrapper(ProxyWrapper):
238
228
"get_paths" ,
239
229
)
240
230
241
- def __init__ (self , data : DataContainer , nus = None , / , ** kwargs ):
242
- super ().__init__ (data , nus )
231
+ def __init__ (self , data : DataContainer , converters = None , / , ** kwargs ):
232
+ super ().__init__ (data , converters )
243
233
self ._wrapped_instance = self ._wrapped_class ([], ** kwargs )
244
234
self ._wrapped_instance .set_transform (mtransforms .IdentityTransform ())
245
235
@@ -262,17 +252,17 @@ class ImageWrapper(ProxyWrapper):
262
252
_wrapped_class = _AxesImage
263
253
required_keys = {"xextent" , "yextent" , "image" }
264
254
265
- def __init__ (self , data : DataContainer , nus = None , / , cmap = None , norm = None , ** kwargs ):
266
- nus = dict ( nus or {})
255
+ def __init__ (self , data : DataContainer , converters = None , / , cmap = None , norm = None , ** kwargs ):
256
+ converters = converters or []
267
257
if cmap is not None or norm is not None :
268
- if nus is not None and "image" in nus :
258
+ if converters is not None and "image" in converters :
269
259
raise ValueError ("Conflicting input" )
270
260
if cmap is None :
271
261
cmap = mpl .colormaps ["viridis" ]
272
262
if norm is None :
273
263
raise ValueError ("not sure how to do autoscaling yet" )
274
- nus [ "image" ] = lambda image : cmap (norm (image ))
275
- super ().__init__ (data , nus )
264
+ converters . append ( FunctionConversionNode . from_funcs ({ "image" : lambda image : cmap (norm (image ))} ))
265
+ super ().__init__ (data , converters )
276
266
kwargs .setdefault ("origin" , "lower" )
277
267
self ._wrapped_instance = self ._wrapped_class (None , ** kwargs )
278
268
@@ -293,8 +283,8 @@ class StepWrapper(ProxyWrapper):
293
283
_privtized_methods = () # ("set_data", "get_data")
294
284
required_keys = {"edges" , "density" }
295
285
296
- def __init__ (self , data : DataContainer , nus = None , / , ** kwargs ):
297
- super ().__init__ (data , nus )
286
+ def __init__ (self , data : DataContainer , converters = None , / , ** kwargs ):
287
+ super ().__init__ (data , converters )
298
288
self ._wrapped_instance = self ._wrapped_class ([], [1 ], ** kwargs )
299
289
300
290
@_stale_wrapper
@@ -312,8 +302,8 @@ class FormatedText(ProxyWrapper):
312
302
_wrapped_class = _Text
313
303
_privtized_methods = ("set_text" ,)
314
304
315
- def __init__ (self , data : DataContainer , nus = None , / , ** kwargs ):
316
- super ().__init__ (data , nus )
305
+ def __init__ (self , data : DataContainer , converters = None , / , ** kwargs ):
306
+ super ().__init__ (data , converters )
317
307
self ._wrapped_instance = self ._wrapped_class (text = "" , ** kwargs )
318
308
319
309
@_stale_wrapper
@@ -368,8 +358,8 @@ class ErrorbarWrapper(MultiProxyWrapper):
368
358
required_keys = {"x" , "y" }
369
359
expected_keys = {f"{ axis } { dirc } " for axis in ["x" , "y" ] for dirc in ["upper" , "lower" ]}
370
360
371
- def __init__ (self , data : DataContainer , nus = None , / , ** kwargs ):
372
- super ().__init__ (data , nus )
361
+ def __init__ (self , data : DataContainer , converters = None , / , ** kwargs ):
362
+ super ().__init__ (data , converters )
373
363
# TODO all of the kwarg teasing apart that is needed
374
364
color = kwargs .pop ("color" , "k" )
375
365
lw = kwargs .pop ("lw" , 2 )
0 commit comments