Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error when loading dataset into image viewer with unrecognized unit #445

Closed
astrofrog opened this issue May 16, 2024 · 0 comments · Fixed by glue-viz/glue#2493
Closed

Error when loading dataset into image viewer with unrecognized unit #445

astrofrog opened this issue May 16, 2024 · 0 comments · Fixed by glue-viz/glue#2493
Labels
bug Something isn't working 🔥 viewer-image

Comments

@astrofrog
Copy link
Member

The following code:

import numpy as np
import glue_jupyter as gj
app = gj.jglue()
data = app.add_data(x=np.random.random((10, 10)))[0]
data.get_component('x').units = 'banana'
app.imshow(data=data)

crashes with:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
File ~/python/dev/lib/python3.11/site-packages/astropy-7.0.0.dev159+g4694350e13-py3.11-linux-x86_64.egg/astropy/units/format/generic.py:585, in Generic._do_parse(cls, s, debug)
    582 try:
    583     # This is a short circuit for the case where the string
    584     # is just a single unit name
--> 585     return cls._parse_unit(s, detailed_exception=False)
    586 except ValueError as e:

File ~/python/dev/lib/python3.11/site-packages/astropy-7.0.0.dev159+g4694350e13-py3.11-linux-x86_64.egg/astropy/units/format/generic.py:481, in Generic._parse_unit(cls, s, detailed_exception)
    480 else:
--> 481     raise ValueError()

ValueError: 

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
File ~/python/dev/lib/python3.11/site-packages/astropy-7.0.0.dev159+g4694350e13-py3.11-linux-x86_64.egg/astropy/units/format/generic.py:451, in Generic._get_unit(cls, t)
    450 try:
--> 451     return cls._parse_unit(t.value)
    452 except ValueError as e:

File ~/python/dev/lib/python3.11/site-packages/astropy-7.0.0.dev159+g4694350e13-py3.11-linux-x86_64.egg/astropy/units/format/generic.py:479, in Generic._parse_unit(cls, s, detailed_exception)
    478 if detailed_exception:
--> 479     raise ValueError(f"{s} is not a valid unit. {did_you_mean(s, registry)}")
    480 else:

ValueError: banana is not a valid unit. 

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
File ~/python/dev/lib/python3.11/site-packages/astropy-7.0.0.dev159+g4694350e13-py3.11-linux-x86_64.egg/astropy/units/core.py:2132, in _UnitMetaClass.__call__(self, s, represents, format, namespace, doc, parse_strict)
   2131 try:
-> 2132     return f.parse(s)
   2133 except NotImplementedError:

File ~/python/dev/lib/python3.11/site-packages/astropy-7.0.0.dev159+g4694350e13-py3.11-linux-x86_64.egg/astropy/units/format/generic.py:569, in Generic.parse(cls, s, debug)
    567     s = cls._regex_deg.sub(cls._convert_deg, s)
--> 569 result = cls._do_parse(s, debug=debug)
    570 # Check for excess solidi, but exclude fractional exponents (accepted)

File ~/python/dev/lib/python3.11/site-packages/astropy-7.0.0.dev159+g4694350e13-py3.11-linux-x86_64.egg/astropy/units/format/generic.py:588, in Generic._do_parse(cls, s, debug)
    587 try:
--> 588     return cls._parser.parse(s, lexer=cls._lexer, debug=debug)
    589 except ValueError as e:

File ~/python/dev/lib/python3.11/site-packages/astropy-7.0.0.dev159+g4694350e13-py3.11-linux-x86_64.egg/astropy/utils/parsing.py:120, in ThreadSafeParser.parse(self, *args, **kwargs)
    119 with self._lock:
--> 120     return self.parser.parse(*args, **kwargs)

File ~/python/dev/lib/python3.11/site-packages/astropy-7.0.0.dev159+g4694350e13-py3.11-linux-x86_64.egg/astropy/extern/ply/yacc.py:333, in LRParser.parse(self, input, lexer, debug, tracking, tokenfunc)
    332 else:
--> 333     return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)

File ~/python/dev/lib/python3.11/site-packages/astropy-7.0.0.dev159+g4694350e13-py3.11-linux-x86_64.egg/astropy/extern/ply/yacc.py:1063, in LRParser.parseopt_notrack(self, input, lexer, debug, tracking, tokenfunc)
   1062 if not lookaheadstack:
-> 1063     lookahead = get_token()     # Get the next token
   1064 else:

File ~/python/dev/lib/python3.11/site-packages/astropy-7.0.0.dev159+g4694350e13-py3.11-linux-x86_64.egg/astropy/extern/ply/lex.py:350, in Lexer.token(self)
    348 self.lexpos = lexpos
--> 350 newtok = func(tok)
    352 # Every function must return a token, if nothing, we just move to next token

File ~/python/dev/lib/python3.11/site-packages/astropy-7.0.0.dev159+g4694350e13-py3.11-linux-x86_64.egg/astropy/units/format/generic.py:123, in Generic._make_lexer.<locals>.t_UNIT(t)
    122 "%|([YZEPTGMkhdcmu\N{MICRO SIGN}npfazy]?'((?!\\d)\\w)+')|((?!\\d)\\w)+"
--> 123 t.value = cls._get_unit(t)
    124 return t

File ~/python/dev/lib/python3.11/site-packages/astropy-7.0.0.dev159+g4694350e13-py3.11-linux-x86_64.egg/astropy/units/format/generic.py:457, in Generic._get_unit(cls, t)
    455     return registry.aliases[t.value]
--> 457 raise ValueError(f"At col {t.lexpos}, {str(e)}")

ValueError: At col 0, banana is not a valid unit. 

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
Cell In[18], line 6
      4 data = app.add_data(x=np.random.random((10, 10)))[0]
      5 data.get_component('x').units = 'banana'
----> 6 app.imshow(data=data)

File ~/Code/glue-jupyter/glue_jupyter/app.py:451, in JupyterApplication.imshow(self, data, x, y, widget, show)
    447 if len(data.pixel_component_ids) < 2:
    448     raise ValueError('Only data with two or more dimensions can be used '
    449                      'as the initial dataset in the image viewer')
--> 451 view = self.new_data_viewer(viewer_cls, data=data, show=show)
    453 if x is not None:
    454     x = data.id[x]

File ~/Code/glue-jupyter/glue_jupyter/app.py:189, in JupyterApplication.new_data_viewer(self, *args, **kwargs)
    187         raise ValueError("No registered viewer found with name {0}".format(viewer_name))
    188     args = (viewer_cls, )
--> 189 viewer = super().new_data_viewer(*args, **kwargs)
    190 self._viewer_refs.append(weakref.ref(viewer))
    191 if show:

File ~/Code/glue/glue/core/application_base.py:87, in Application.new_data_viewer(self, viewer_class, data, state)
     85 if data is not None:
     86     if isinstance(data, BaseData):
---> 87         result = c.add_data(data)
     88     elif isinstance(data, Subset):
     89         result = c.add_subset(data)

File ~/Code/glue-jupyter/glue_jupyter/view.py:118, in IPyWidgetView.add_data(self, data, color, alpha, **layer_state)
    114 def add_data(self, data, color=None, alpha=None, **layer_state):
    116     data = validate_data_argument(self._data, data)
--> 118     result = super().add_data(data)
    120     if not result:
    121         return

File ~/Code/glue/glue/viewers/common/viewer.py:209, in Viewer.add_data(self, data)
    205     raise IncompatibleDataException("Data not in DataCollection")
    207 # Create layer artist and add to container. First check whether any
    208 # plugins want to make a custom layer artist.
--> 209 layer = get_layer_artist_from_registry(data, self) or self.get_data_layer_artist(data)
    211 if layer is None:
    212     return False

File ~/Code/glue-jupyter/glue_jupyter/bqplot/image/viewer.py:101, in BqplotImageView.get_data_layer_artist(self, layer, layer_state)
     99 else:
    100     cls = BqplotImageLayerArtist
--> 101 return self.get_layer_artist(cls, layer=layer, layer_state=layer_state)

File ~/Code/glue-jupyter/glue_jupyter/view.py:143, in IPyWidgetView.get_layer_artist(self, cls, layer, layer_state)
    142 def get_layer_artist(self, cls, layer=None, layer_state=None):
--> 143     return cls(self, self.state, layer=layer, layer_state=layer_state)

File ~/Code/glue-jupyter/glue_jupyter/bqplot/image/layer_artist.py:22, in BqplotImageLayerArtist.__init__(self, view, *args, **kwargs)
     21 def __init__(self, view, *args, **kwargs):
---> 22     super().__init__(view, *args, **kwargs)
     23     # TODO: we should probably use a lru cache to avoid having a 'memleak'
     24     self._contour_line_cache = {}

File ~/Code/glue/glue/viewers/image/layer_artist.py:68, in ImageLayerArtist.__init__(self, axes, viewer_state, layer_state, layer)
     66 def __init__(self, axes, viewer_state, layer_state=None, layer=None):
---> 68     super(ImageLayerArtist, self).__init__(axes, viewer_state,
     69                                            layer_state=layer_state, layer=layer)
     71     # We use a custom object to deal with the compositing of images, and we
     72     # store it as a private attribute of the axes to make sure it is
     73     # accessible for all layer artists.
     74     self.uuid = str(uuid.uuid4())

File ~/Code/glue/glue/viewers/image/layer_artist.py:31, in BaseImageLayerArtist.__init__(self, axes, viewer_state, layer_state, layer)
     29 def __init__(self, axes, viewer_state, layer_state=None, layer=None):
---> 31     super(BaseImageLayerArtist, self).__init__(axes, viewer_state,
     32                                                layer_state=layer_state, layer=layer)
     34     # Watch for changes in the viewer state which would require the
     35     # layers to be redrawn
     36     self._viewer_state.add_global_callback(self._update_image)

File ~/Code/glue/glue/viewers/matplotlib/layer_artist.py:18, in MatplotlibLayerArtist.__init__(self, axes, viewer_state, layer_state, layer)
     16 def __init__(self, axes, viewer_state, layer_state=None, layer=None):
---> 18     super(MatplotlibLayerArtist, self).__init__(viewer_state,
     19                                                 layer_state=layer_state,
     20                                                 layer=layer)
     21     self.axes = axes
     22     self.mpl_artists = []

File ~/Code/glue/glue/viewers/common/layer_artist.py:25, in LayerArtist.__init__(self, viewer_state, layer_state, layer)
     22 self._viewer_state = viewer_state
     24 self.layer = layer or layer_state.layer
---> 25 self.state = layer_state or self._layer_state_cls(viewer_state=viewer_state,
     26                                                   layer=self.layer)
     28 if self.state not in self._viewer_state.layers:
     29     self._viewer_state.layers.append(self.state)

File ~/Code/glue-jupyter/glue_jupyter/bqplot/image/state.py:36, in BqplotImageLayerState.__init__(self, *args, **kwargs)
     34 def __init__(self, *args, **kwargs):
---> 36     super(BqplotImageLayerState, self).__init__(*args, **kwargs)
     38     BqplotImageLayerState.level_mode.set_choices(self, ['Linear', 'Custom'])
     39     percentile_display = {100: 'Min/Max',
     40                           99.5: '99.5%',
     41                           99: '99%',
     42                           95: '95%',
     43                           90: '90%',
     44                           'Custom': 'Custom'}

File ~/Code/glue/glue/viewers/image/state.py:553, in ImageLayerState.__init__(self, layer, viewer_state, **kwargs)
    550 self._update_syncing()
    552 if layer is not None:
--> 553     self._update_attribute()
    555 self.update_from_dict(kwargs)
    557 if self.cmap is None:

File ~/Code/glue/glue/viewers/image/state.py:562, in ImageLayerState._update_attribute(self, *args)
    560 def _update_attribute(self, *args):
    561     if self.layer is not None:
--> 562         self.attribute_att_helper.set_multiple_data([self.layer])
    563         self.attribute = self.layer.main_components[0]

File ~/Code/glue/glue/core/data_combo_helper.py:322, in ComponentIDComboHelper.set_multiple_data(self, datasets)
    320 for data in unique_data_iter(datasets):
    321     self.append_data(data, refresh=False)
--> 322 self.refresh()

File ~/Code/glue/glue/core/data_combo_helper.py:379, in ComponentIDComboHelper.refresh(self, *args)
    376         if len(cids) > 1:
    377             choices += cids
--> 379 self.choices = choices

File ~/Code/glue/glue/core/data_combo_helper.py:84, in ComboHelper.choices(self, choices)
     82 @choices.setter
     83 def choices(self, choices):
---> 84     with delay_callback(self.state, self.selection_property):
     85         prop = getattr(type(self.state), self.selection_property)
     86         prop.set_choices(self.state, choices)

File ~/python/dev/lib/python3.11/site-packages/echo/core.py:538, in delay_callback.__exit__(self, *args)
    535     self.instance._process_delayed_global_callbacks(resume_props)
    537 for p, args in notifications:
--> 538     p.notify(*args)

File ~/Code/glue/glue/utils/matplotlib.py:180, in defer_draw.<locals>.wrapper(*args, **kwargs)
    178     for backend in DEFER_DRAW_BACKENDS:
    179         backend.draw_idle = DeferredMethod(backend.draw_idle)
--> 180     result = func(*args, **kwargs)
    181 finally:
    182     for backend in DEFER_DRAW_BACKENDS:
    183         # We need to use another try...finally block here in case the
    184         # executed deferred draw calls fail for any reason

File ~/Code/glue/glue/viewers/matplotlib/state.py:38, in DeferredDrawSelectionCallbackProperty.notify(self, *args, **kwargs)
     36 @defer_draw
     37 def notify(self, *args, **kwargs):
---> 38     super(DeferredDrawSelectionCallbackProperty, self).notify(*args, **kwargs)

File ~/python/dev/lib/python3.11/site-packages/echo/core.py:125, in CallbackProperty.notify(self, instance, old, new)
    123     return
    124 for cback in self._callbacks.get(instance, []):
--> 125     cback(new)
    126 for cback in self._2arg_callbacks.get(instance, []):
    127     cback(old, new)

File ~/Code/glue/glue/viewers/image/state.py:633, in ImageLayerState._update_attribute_display_unit_choices(self, *args)
    631     c_choices = ['']
    632 ImageLayerState.attribute_display_unit.set_choices(self, c_choices)
--> 633 self.attribute_display_unit = component.units

File ~/python/dev/lib/python3.11/site-packages/echo/core.py:263, in HasCallbackProperties.__setattr__(self, attribute, value)
    261 super(HasCallbackProperties, self).__setattr__(attribute, value)
    262 if self.is_callback_property(attribute):
--> 263     self._notify_global(**{attribute: value})

File ~/Code/glue/glue/utils/matplotlib.py:175, in defer_draw.<locals>.wrapper(*args, **kwargs)
    172 # Don't recursively defer draws. We just check the first draw_idle
    173 # method since all should be modified in sync.
    174 if isinstance(DEFER_DRAW_BACKENDS[0].draw_idle, DeferredMethod):
--> 175     return func(*args, **kwargs)
    177 try:
    178     for backend in DEFER_DRAW_BACKENDS:

File ~/Code/glue/glue/viewers/matplotlib/state.py:319, in MatplotlibLayerState._notify_global(self, *args, **kwargs)
    317 @defer_draw
    318 def _notify_global(self, *args, **kwargs):
--> 319     super(MatplotlibLayerState, self)._notify_global(*args, **kwargs)

File ~/python/dev/lib/python3.11/site-packages/echo/core.py:258, in HasCallbackProperties._notify_global(self, **kwargs)
    256 if len(kwargs) > 0:
    257     for callback in self._global_callbacks:
--> 258         callback(**kwargs)

File ~/Code/glue/glue/core/state_objects.py:207, in StateAttributeCacheHelper._update_values(self, **properties)
    204 properties = dict((self._attribute_lookup_inv[key], value)
    205                   for key, value in properties.items() if key in self._attribute_lookup_inv and self._attribute_lookup_inv[key] != 'attribute')
    206 if len(properties) > 0:
--> 207     self.update_values(**properties)

File ~/Code/glue/glue/core/state_objects.py:400, in StateAttributeLimitsHelper.update_values(self, force, use_default_modifiers, **properties)
    398     limits = np.hstack([lower, upper])
    399     converter = UnitConverter()
--> 400     lower, upper = converter.to_unit(self.data, self.component_id,
    401                                      np.hstack([lower, upper]),
    402                                      display_units)
    404 if log:
    405     value_range = np.log10(upper / lower)

File ~/Code/glue/glue/core/units.py:31, in UnitConverter.to_unit(self, data, cid, values, target_units)
     29 original_units = self._get_units(data, cid)
     30 if original_units:
---> 31     return self.converter_helper.to_unit(data, cid, values, original_units, target_units)
     32 else:
     33     return values

File ~/Code/glue/glue/core/units.py:56, in SimpleAstropyUnitConverter.to_unit(self, data, cid, values, original_units, target_units)
     55 def to_unit(self, data, cid, values, original_units, target_units):
---> 56     return (values * u.Unit(original_units)).to_value(target_units)

File ~/python/dev/lib/python3.11/site-packages/astropy-7.0.0.dev159+g4694350e13-py3.11-linux-x86_64.egg/astropy/units/core.py:2155, in _UnitMetaClass.__call__(self, s, represents, format, namespace, doc, parse_strict)
   2145 msg = (
   2146     f"'{s}' did not parse as {format_clause}unit: {str(e)} "
   2147     "If this is meant to be a custom unit, "
   (...)
   2152     "https://docs.astropy.org/en/latest/units/combining_and_defining.html"
   2153 )
   2154 if parse_strict == "raise":
-> 2155     raise ValueError(msg)
   2156 elif parse_strict == "warn":
   2157     warnings.warn(msg, UnitsWarning)

ValueError: 'banana' did not parse as unit: At col 0, banana is not a valid unit.  If this is meant to be a custom unit, define it with 'u.def_unit'. To have it recognized inside a file reader or other code, enable it with 'u.add_enabled_units'. For details, see https://docs.astropy.org/en/latest/units/combining_and_defining.html

I'm not sure why this is happening since by default we should just show native units, and also we should make sure we don't crash like this with unrecognized units.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working 🔥 viewer-image
Projects
None yet
1 participant