astropy:docs

Source code for astropy.nddata.decorators

# Licensed under a 3-clause BSD style license - see LICENSE.rst

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import inspect
import warnings

from ..utils import wraps
from ..utils.exceptions import AstropyUserWarning

from .nddata import NDData

__all__ = ['support_nddata']


[docs]def support_nddata(_func=None, accepts=NDData, repack=False, returns=None): """ Decorator to split NDData properties into function arguments. This is a decorator to allow functions to take NDData objects as their first arguments and split up the properties into kwargs as required by the function. For example, if you consider the following function:: def downsample(data, wcs=None): # downsample data and optionally WCS here pass This function takes a Numpy array for the data, and some WCS information with the ``data`` keyword argument. However, you might have an NDData instance that has the ``wcs`` property set and you would like to be able to call the function with ``downsample(my_nddata)`` and have the WCS information, if present, automatically be passed to the ``wcs`` keyword argument. This decorator can be used to make this possible:: @support_nddata def downsample(data, wcs=None): # downsample data and optionally WCS here pass This function can now either be called as before, specifying the data and WCS separately, or an NDData instance can be passed to the ``data`` argument. The restrictions on functions to use this function are: * The first positional argument should be ``data`` and take a Numpy array. * The following arguments can optionally be specified in the function signature, but if they are specified they should be keyword arguments: ``uncertainty``, ``mask``, ``meta``, ``unit``, and ``wcs``. If you are making use of this decorator, you should be prepared for these keyword arguments to be set to the properties of the NDData object (if present). The behavior of the decorator is to check through the NDData properties and if they are set, it checks if the function accepts them as keyword arguments. If an NDData property is set but cannot be passed to a keyword argument, a warning is emitted to tell the user that the NDData property in question will not be used by the function (to ensure that they know when e.g. uncertainties cannot be used). If the user passes an NDData object *and* explicitly sets a keyword argument that is one of the valid NDData properties, a warning is emitted to inform the user that the explicitly specified value will take priority. """ if returns is not None and not repack: raise ValueError('returns should only be set if repack=True') if returns is None and repack: raise ValueError('returns should be set if repack=True') def support_nddata_decorator(func): # Find out args and kwargs wrapped_argspec = inspect.getargspec(func) # Find out the args and kwargs if wrapped_argspec.defaults: func_args = wrapped_argspec.args[:-len(wrapped_argspec.defaults)] func_kwargs = wrapped_argspec.args[len(func_args):] else: func_args = wrapped_argspec.args func_kwargs = [] # First argument should be data if len(func_args) == 0 or func_args[0] != 'data': raise ValueError("Can only wrap functions whose first positional argument is `data`") supported_properties = ['uncertainty', 'mask', 'meta', 'unit', 'wcs'] @wraps(func) def wrapper(data, *args, **kwargs): unpack = isinstance(data, accepts) input_data = data if not unpack and isinstance(data, NDData): raise TypeError("Only NDData sub-classes that inherit from {0}" " can be used by this function".format(accepts.__name__)) # If data is an NDData instance, we can try and find properties that # can be passed as kwargs. if unpack: ignored = [] # We loop over a list of pre-defined properties for prop in supported_properties: # We only need to do something if the property exists on the # NDData object if hasattr(data, prop): value = getattr(data, prop) if (prop == 'meta' and len(value) > 0) or (prop != 'meta' and value is not None): if prop in func_kwargs: if prop in kwargs and kwargs[prop] is not None: warnings.warn("Property {0} has been passed explicitly and as an " "NDData property, using explicitly specified value".format(prop), AstropyUserWarning) else: kwargs[prop] = value else: ignored.append(prop) if ignored: warnings.warn("The following attributes were set on the data object, " "but will be ignored by the function: " + ", ".join(ignored), AstropyUserWarning) # Finally, replace data by the data itself data = data.data result = func(data, *args, **kwargs) if unpack: if repack: if len(returns) > 1 and len(returns) != len(result): raise ValueError("Function did not return the expected number of arguments") elif len(returns) == 1: result = [result] return input_data.__class__(**dict(zip(returns, result))) else: return result else: return result return wrapper # If _func is set, this means that the decorator was used without # parameters so we have to return the result of the support_nddata_decorator # decorator rather than the decorator itself if _func is not None: return support_nddata_decorator(_func) else: return support_nddata_decorator

Page Contents