1616from matplotlib .artist import Artist as _Artist
1717
1818from 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+ )
1926
2027
2128class _BBox (Protocol ):
@@ -139,45 +146,26 @@ def _query_and_transform(self, renderer, *, xunits: List[str], yunits: List[str]
139146 return self ._cache [cache_key ]
140147 except KeyError :
141148 ...
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 )
157151
158152 self ._cache [cache_key ] = transformed_data
159153 return transformed_data
160154
161- def __init__ (self , data , nus , ** kwargs ):
155+ def __init__ (self , data , converters : ConversionNode | list [ ConversionNode ] | None , ** kwargs ):
162156 super ().__init__ (** kwargs )
163157 self .data = data
164158 self ._cache = LFUCache (64 )
165159 # 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 ))
174167 self .stale = True
175168
176- # TODO add a setter
177- @property
178- def nus (self ):
179- return dict (self ._nus )
180-
181169
182170class ProxyWrapper (ProxyWrapperBase ):
183171 _privtized_methods : Tuple [str , ...] = ()
@@ -192,7 +180,7 @@ def __getattr__(self, key):
192180 return getattr (self ._wrapped_instance , key )
193181
194182 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" ):
196184 super ().__setattr__ (key , value )
197185 elif hasattr (self , "_wrapped_instance" ) and hasattr (self ._wrapped_instance , key ):
198186 setattr (self ._wrapped_instance , key , value )
@@ -205,9 +193,12 @@ class LineWrapper(ProxyWrapper):
205193 _privtized_methods = ("set_xdata" , "set_ydata" , "set_data" , "get_xdata" , "get_ydata" , "get_data" )
206194 required_keys = {"x" , "y" }
207195
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 )
210198 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 )
211202
212203 @_stale_wrapper
213204 def draw (self , renderer ):
@@ -218,7 +209,6 @@ def draw(self, renderer):
218209
219210 def _update_wrapped (self , data ):
220211 for k , v in data .items ():
221- k = {"x" : "xdata" , "y" : "ydata" }.get (k , k )
222212 getattr (self ._wrapped_instance , f"set_{ k } " )(v )
223213
224214
@@ -238,8 +228,8 @@ class PathCollectionWrapper(ProxyWrapper):
238228 "get_paths" ,
239229 )
240230
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 )
243233 self ._wrapped_instance = self ._wrapped_class ([], ** kwargs )
244234 self ._wrapped_instance .set_transform (mtransforms .IdentityTransform ())
245235
@@ -262,17 +252,17 @@ class ImageWrapper(ProxyWrapper):
262252 _wrapped_class = _AxesImage
263253 required_keys = {"xextent" , "yextent" , "image" }
264254
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 []
267257 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 :
269259 raise ValueError ("Conflicting input" )
270260 if cmap is None :
271261 cmap = mpl .colormaps ["viridis" ]
272262 if norm is None :
273263 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 )
276266 kwargs .setdefault ("origin" , "lower" )
277267 self ._wrapped_instance = self ._wrapped_class (None , ** kwargs )
278268
@@ -293,8 +283,8 @@ class StepWrapper(ProxyWrapper):
293283 _privtized_methods = () # ("set_data", "get_data")
294284 required_keys = {"edges" , "density" }
295285
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 )
298288 self ._wrapped_instance = self ._wrapped_class ([], [1 ], ** kwargs )
299289
300290 @_stale_wrapper
@@ -312,8 +302,8 @@ class FormatedText(ProxyWrapper):
312302 _wrapped_class = _Text
313303 _privtized_methods = ("set_text" ,)
314304
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 )
317307 self ._wrapped_instance = self ._wrapped_class (text = "" , ** kwargs )
318308
319309 @_stale_wrapper
@@ -368,8 +358,8 @@ class ErrorbarWrapper(MultiProxyWrapper):
368358 required_keys = {"x" , "y" }
369359 expected_keys = {f"{ axis } { dirc } " for axis in ["x" , "y" ] for dirc in ["upper" , "lower" ]}
370360
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 )
373363 # TODO all of the kwarg teasing apart that is needed
374364 color = kwargs .pop ("color" , "k" )
375365 lw = kwargs .pop ("lw" , 2 )
0 commit comments