Source code for jdaviz.core.user_api

import warnings

import astropy.units as u
from astropy.utils.exceptions import AstropyDeprecationWarning


__all__ = ['UserApiWrapper', 'PluginUserApi',
           'LoaderUserApi', 'ImporterUserApi',
           'ViewerUserApi', 'ViewerWindowUserApi',
           'DataApi', 'SpectralDataApi', 'SpatialDataApi',
           'TemporalSpatialDataApi', 'SpectralSpatialDataApi']

_internal_attrs = ('_obj', '_expose', '_expose_nested', '_items', '_readonly', '_exclude_from_dict',
                   '__doc__', '_deprecation_msg', '_deprecated', '_repr_callable')


[docs] class DataApi: def __init__(self, app, data_label): self._app = app self._data_label = data_label def __repr__(self): return f'<Data API for {self._data_label}>'
[docs] def get_data(self, cls=None, use_display_units=False): return self._app._jdaviz_helper._get_data(self._data_label, cls=cls, use_display_units=use_display_units)
[docs] def add_to_viewer(self, viewer_label): viewer = self._app._jdaviz_helper.viewers.get(viewer_label) if viewer is None: raise ValueError(f"{viewer_label} is not a valid viewer. Valid viewers are: {list(self._app._jdaviz_helper.viewers.keys())}") # noqa dm = viewer.data_menu unloaded_data = dm._obj.dataset.choices if self._data_label in dm.layer.choices: raise ValueError(f"{self._data_label} is already loaded into {viewer_label}. Valid data to be loaded are: {unloaded_data}") # noqa if self._data_label not in unloaded_data: raise ValueError(f"{self._data_label} is not one of the valid data to be loaded into {viewer_label}. Valid data are: {unloaded_data}") # noqa dm.add_data(self._data_label)
[docs] class SpectralDataApi(DataApi): """DataApi for spectral data that supports spectral subsets."""
[docs] def get_data(self, spectral_subset=None, cls=None, use_display_units=False): """ Get the data as an object with optional spectral subset applied. Parameters ---------- spectral_subset : str, optional Spectral subset to apply to the data. cls : class, optional The type to return the data as. use_display_units : bool, optional Whether to convert to display units. Returns ------- data : object The data object with subset applied. """ return self._app._jdaviz_helper._get_data(self._data_label, spectral_subset=spectral_subset, cls=cls, use_display_units=use_display_units)
[docs] class SpatialDataApi(DataApi): """DataApi for spatial/image data that supports spatial subsets."""
[docs] def get_data(self, spatial_subset=None, cls=None, use_display_units=False): """ Get the data as an object with optional spatial subset applied. Parameters ---------- spatial_subset : str, optional Spatial subset to apply to the data. cls : class, optional The type to return the data as. use_display_units : bool, optional Whether to convert to display units. Returns ------- data : object The data object with subset applied. """ return self._app._jdaviz_helper._get_data(self._data_label, spatial_subset=spatial_subset, cls=cls, use_display_units=use_display_units)
[docs] class TemporalSpatialDataApi(DataApi): """DataApi for ramp data that supports spatial and temporal subsets."""
[docs] def get_data(self, spatial_subset=None, temporal_subset=None, cls=None, use_display_units=False): """ Get the data as an object with optional spatial and temporal subsets applied. Parameters ---------- spatial_subset : str, optional Spatial subset to apply to the data. temporal_subset : str, optional Temporal subset to apply to the data. cls : class, optional The type to return the data as. use_display_units : bool, optional Whether to convert to display units. Returns ------- data : object The data object with subsets applied. """ return self._app._jdaviz_helper._get_data(self._data_label, spatial_subset=spatial_subset, temporal_subset=temporal_subset, cls=cls, use_display_units=use_display_units)
[docs] class SpectralSpatialDataApi(DataApi): """DataApi for cube data that supports both spectral and spatial subsets."""
[docs] def get_data(self, spatial_subset=None, spectral_subset=None, cls=None, use_display_units=False): """ Get the data as an object with optional spatial and spectral subsets applied. Parameters ---------- spatial_subset : str, optional Spatial subset to apply to the data. spectral_subset : str, optional Spectral subset to apply to the data. cls : class, optional The type to return the data as. use_display_units : bool, optional Whether to convert to display units. Returns ------- data : object The data object with subsets applied. """ return self._app._jdaviz_helper._get_data(self._data_label, spatial_subset=spatial_subset, spectral_subset=spectral_subset, cls=cls, use_display_units=use_display_units)
[docs] class UserApiWrapper: """ This is an API wrapper around an internal object. For a full list of attributes/methods, call dir(object). """ def __init__(self, obj, expose=[], readonly=[], exclude_from_dict=[], deprecated=[], repr_callable=None): self._obj = obj self._expose = list(expose) + list(readonly) self._readonly = readonly self._exclude_from_dict = exclude_from_dict self._deprecation_msg = None self._deprecated = deprecated self._repr_callable = repr_callable if obj.__doc__ is not None: self.__doc__ = self.__doc__ + "\n\n\n" + obj.__doc__ def __dir__(self): return self._expose def __repr__(self): if self._repr_callable is not None: return self._repr_callable() return f'<{self._obj.__class__.__name__} API>' def __eq__(self, other): return self._obj.__eq__(other) def __getattr__(self, attr): if attr in _internal_attrs or attr not in self._expose: return super().__getattribute__(attr) if attr in self._deprecated: warnings.warn(f"{attr} is deprecated", DeprecationWarning) exp_obj = getattr(self._obj, attr) return getattr(exp_obj, 'user_api', exp_obj) def __setattr__(self, attr, value): if attr in _internal_attrs: return super().__setattr__(attr, value) if attr not in self._expose: raise ValueError(f"{attr} is not a valid attribute and cannot be set") if attr in self._readonly: raise AttributeError("cannot set read-only item") exp_obj = getattr(self._obj, attr) if hasattr(exp_obj, '__call__'): raise AttributeError(f"{attr} is a callable, cannot set to a value. See help({attr}) for input arguments.") # noqa from jdaviz.core.template_mixin import (SelectPluginComponent, UnitSelectPluginComponent, SelectFileExtensionComponent, PlotOptionsSyncState, AddResults, AutoTextField, ViewerSelectCreateNew) if isinstance(exp_obj, ViewerSelectCreateNew): from jdaviz.utils import has_wildcard if (value in exp_obj.choices + ['', []] or isinstance(value, list) or (isinstance(value, str) and has_wildcard(value))): exp_obj.create_new.selected = '' exp_obj.selected = value return elif len(exp_obj.create_new.choices) > 0: exp_obj.create_new.selected = exp_obj.create_new.choices[0] exp_obj.new_label.value = value return if isinstance(exp_obj, SelectPluginComponent): # this allows setting the selection directly without needing to access the underlying # .selected traitlet if isinstance(exp_obj, UnitSelectPluginComponent) and isinstance(value, u.Unit): value = value.to_string() elif isinstance(exp_obj, SelectFileExtensionComponent): def to_choice_single(value): if isinstance(value, int): # allow setting by index return exp_obj.choices[exp_obj.indices.index(value)] elif isinstance(value, str): # allow setting without index, by: name or name,ver or [name,ver] if value not in exp_obj.choices: value_no_brackets = value.strip('[]') for attr in ('names', 'name_vers'): if value_no_brackets in getattr(exp_obj, attr): index = getattr(exp_obj, attr).index(value_no_brackets) return exp_obj.choices[index] return value else: raise ValueError(f"Invalid value type: {type(value)}") if isinstance(value, (list, tuple)): # allow setting by list of indices or names value = [to_choice_single(v) for v in value] else: # allow setting by single index or name value = to_choice_single(value) exp_obj.selected = value return elif isinstance(exp_obj, AddResults): exp_obj.auto_label.value = value return elif isinstance(exp_obj, AutoTextField): if value != exp_obj.default: exp_obj.auto = False exp_obj.value = value return elif isinstance(exp_obj, PlotOptionsSyncState): if not len(exp_obj.linked_states): raise ValueError("there are currently no synced glue states to set") # this allows setting the value immediately, and unmixing state, if appropriate, # even if the value matches the current value if value == exp_obj.value: exp_obj.unmix_state() else: # if there are choices, allow either passing the text or value text_to_value = {choice['text']: choice['value'] for choice in exp_obj.sync.get('choices', [])} exp_obj.value = text_to_value.get(value, value) return return setattr(self._obj, attr, value) def _items(self): for attr in self._expose: try: yield attr, self.__getattr__(attr) except AttributeError: continue
[docs] def to_dict(self): def _value(item): if hasattr(item, 'to_dict'): return _value(item.to_dict()) if hasattr(item, 'selected'): return item.selected # Handle AutoTextField objects by returning their value from jdaviz.core.template_mixin import AutoTextField if isinstance(item, AutoTextField): return item.value return item return {k: _value(getattr(self, k)) for k in self._expose if k not in ('api_hints_enabled', 'keep_active') and k not in self._exclude_from_dict and not hasattr(getattr(self, k), '__call__')}
[docs] def from_dict(self, d): # loop through expose so that plugins can dictate the order that items should be populated for k in self._expose: if k not in d: continue # Skip readonly attributes - they shouldn't be set from dict if k in self._readonly: continue v = d.get(k) if hasattr(getattr(self, k), '__call__'): raise ValueError(f"cannot overwrite callable {k}") if hasattr(getattr(self, k), 'from_dict') and isinstance(v, dict): getattr(self, k).from_dict(v) else: setattr(self, k, v)
[docs] class PluginUserApi(UserApiWrapper): """ This is an API wrapper around an internal plugin. For a full list of attributes/methods, call dir(plugin_object) and for help on any of those methods, call help(plugin_object.attribute). For example:: help(plugin_object.show) """ def __init__(self, plugin, expose=[], readonly=[], excl_from_dict=[], deprecated=[], in_tray=True): if in_tray: default = ['open_in_tray', 'close_in_tray', 'show'] else: default = ['show'] if hasattr(plugin, 'loaders'): default += ['loaders'] expose = list(set(list(expose) + default)) if plugin.uses_active_status: expose += ['keep_active', 'as_active'] self._deprecation_msg = None super().__init__(plugin, expose, readonly, excl_from_dict, deprecated) def __repr__(self): if self._deprecation_msg: warnings.warn(self._deprecation_msg, AstropyDeprecationWarning) super().__setattr__('_deprecation_msg', None) return f'<{self._obj._registry_label} API>' def __setattr__(self, *args, **kwargs): if hasattr(self, '_deprecation_msg') and self._deprecation_msg: warnings.warn(self._deprecation_msg, AstropyDeprecationWarning) super().__setattr__('_deprecation_msg', None) return super().__setattr__(*args, **kwargs)
[docs] class LoaderUserApi(UserApiWrapper): """ This is an API wrapper around an internal loader/resolver. For a full list of attributes/methods, call dir(loader_object) and for help on any of those methods, call ``help(loader_object.attribute)``. For example:: help(loader_object.show) """ def __init__(self, loader, expose=[], readonly=[], excl_from_dict=[], deprecated=[]): expose = list(set(list(expose) + ['format', 'target', 'importer', 'load', 'show', 'open_in_tray', 'close_in_tray', 'treat_table_as_query', 'observation_table', 'file_table', 'file_cache', 'file_timeout', 'file_local_path', 'enable_footprint_selection_tools', 'disable_footprint_selection_tools'])) super().__init__(loader, expose, readonly, excl_from_dict, deprecated) def __repr__(self): return f'<{self._obj._registry_label} API>' def __setattr__(self, attr, value): result = super().__setattr__(attr, value) if attr not in _internal_attrs: resolver_err = getattr(self._obj, 'parsed_input_is_resolvable', '') if resolver_err: raise ValueError(resolver_err) return result
[docs] class ImporterUserApi(UserApiWrapper): """ This is an API wrapper around an internal importer. For a full list of attributes/methods, call dir(importer_object) and for help on any of those methods, call ``help(importer_object.attribute)``. For example:: help(importer_object.show) """ def __init__(self, importer, expose=[], readonly=[], excl_from_dict=[], deprecated=[]): expose = list(set(list(expose) + ['input', 'output', 'target', 'show'])) for attr in ('viewer', 'viewer_label', 'data_label'): if hasattr(importer, attr): expose += [attr] super().__init__(importer, expose, readonly, excl_from_dict, deprecated)
[docs] def __call__(self, *args, **kwargs): return self._obj(*args, **kwargs)
def __repr__(self): return f'<{self._obj._registry_label} API>'
class ViewerCreatorUserApi(UserApiWrapper): """ This is an API wrapper around an internal new viewer creator. For a full list of attributes/methods, call dir(viewer_creator_object) and for help on any of those methods, call ``help(viewer_creator_object.attribute)``. For example:: help(viewer_creator_object.show) """ def __init__(self, importer, expose=[], readonly=[], excl_from_dict=[], deprecated=[]): expose = list(set(list(expose) + ['dataset', 'viewer_label', 'show', 'open_in_tray', 'close_in_tray'])) super().__init__(importer, expose, readonly, excl_from_dict, deprecated) def __call__(self): return self._obj() def __repr__(self): return f'<{self._obj._registry_label} API>'
[docs] class ViewerUserApi(UserApiWrapper): """ This is an API wrapper around a viewer. For a full list of attributes/methods, call dir(viewer_object) and for help on any of those methods, call help(viewer_object.attribute). For example:: help(viewer_object.show) """ def __init__(self, viewer, expose=[], readonly=[], excl_from_dict=[], deprecated=[]): expose = list(set(list(expose) + [])) super().__init__(viewer, expose, readonly, excl_from_dict, deprecated) def __repr__(self): return f'<{self._obj.reference} API>' def __getattr__(self, *args, **kwargs): if super().__getattr__('_deprecation_msg'): warnings.warn(self._deprecation_msg, AstropyDeprecationWarning) super().__setattr__('_deprecation_msg', None) return super().__getattr__(*args, **kwargs) def __setattr__(self, *args, **kwargs): if hasattr(self, '_deprecation_msg') and self._deprecation_msg: warnings.warn(self._deprecation_msg, AstropyDeprecationWarning) super().__setattr__('_deprecation_msg', None) return super().__setattr__(*args, **kwargs)
[docs] class ViewerWindowUserApi(UserApiWrapper): """ This is an API wrapper around a viewer. For a full list of attributes/methods, call dir(viewer_object) and for help on any of those methods, call help(viewer_object.attribute). For example:: help(viewer_object.show) """ def __init__(self, viewer_container, expose=[], readonly=[], excl_from_dict=[], deprecated=[]): self._expose_nested = viewer_container.glue_viewer.user_api._expose expose = list(set(list(expose) + self._expose_nested)) super().__init__(viewer_container, expose, readonly, excl_from_dict, deprecated) def __repr__(self): return f'<{self._obj.glue_viewer.reference} API>' def __getattr__(self, attr): if attr in _internal_attrs: return super().__getattribute__(attr) if attr in self._expose_nested: return self._obj.glue_viewer.user_api.__getattr__(attr) else: return super().__getattr__(attr) def __setattr__(self, attr, value): if attr in _internal_attrs: return super().__setattr__(attr, value) if attr in self._expose_nested: return self._obj.glue_viewer.user_api.__setattr__(attr, value) else: return super().__setattr__(attr, value)