Source code for ndcube.visualization.descriptor

import functools

MISSING_MATPLOTLIB_ERROR_MSG = ("matplotlib cannot be imported, so the default plotting "
                                "functionality is disabled. Please install matplotlib")
MISSING_ANIMATORS_ERROR_MSG = ("mpl_animators cannot be imported, so the default plotting "
                               "functionality is disabled. Please install mpl_animators")

__all__ = ['PlotterDescriptor', 'MISSING_MATPLOTLIB_ERROR_MSG', 'MISSING_ANIMATORS_ERROR_MSG']


[docs] class PlotterDescriptor: def __init__(self, default_type=None): self._default_type = default_type def __set_name__(self, owner, name): """ This function is called when the class the descriptor is attached to is initialized. The *class* and not the instance. """ # property name is the name of the attribute on the parent class # pointing at an instance of this descriptor. self._property_name = name # attribute name is the name of the attribute on the parent class where # the data is stored. self._attribute_name = f"_{name}" plotter = self._resolve_default_type(raise_error=False) if plotter is not None and hasattr(plotter, "plot"): functools.update_wrapper(owner.plot, plotter.plot) def _resolve_default_type(self, raise_error=True): # We special case the default MatplotlibPlotter so that we can # delay the import of matplotlib until the plotter is first # accessed. if self._default_type in ("mpl_plotter", "mpl_sequence_plotter"): if self._default_type == "mpl_plotter": try: from ndcube.visualization.mpl_plotter import MatplotlibPlotter return MatplotlibPlotter except ImportError as e: if raise_error: raise ImportError(MISSING_MATPLOTLIB_ERROR_MSG) from e elif self._default_type == "mpl_sequence_plotter": try: from ndcube.visualization.mpl_sequence_plotter import MatplotlibSequencePlotter return MatplotlibSequencePlotter except ImportError as e: if raise_error: raise ImportError(MISSING_ANIMATORS_ERROR_MSG) from e elif self._default_type is not None: return self._default_type # If we have no default type then just return None else: return def __get__(self, obj, objtype=None): if obj is None: return if getattr(obj, self._attribute_name, None) is None: plotter_type = self._resolve_default_type() if plotter_type is None: return self.__set__(obj, plotter_type) return getattr(obj, self._attribute_name) def __set__(self, obj, value): if not isinstance(value, type): raise TypeError( "Plotter attribute can only be set with an uninitialised plotter object.") setattr(obj, self._attribute_name, value(obj)) # here obj is the ndcube object and value is the plotter type # Get the instantiated plotter we just assigned to the ndcube plotter = getattr(obj, self._attribute_name) # If the plotter has a plot object then update the signature and # docstring of the cubes `plot()` method to match # Docstrings of methods aren't writeable so we copy to the underlying # function object instead if hasattr(plotter, "plot"): functools.update_wrapper(obj.plot.__func__, plotter.plot.__func__)