astropy/astropy — pull request #2855 by astrofrog (2014-08-14 19:56:02+00:00)
Title: WIP: decorator for functions that want to accept NDData

As discussed during the NDData telecon today, rather than having to provide two interfaces for users - one that accepts separate arguments and one that accepts NDData - it would be nice to be able to write functions with explicit arguments that are needed, such as:

```
def test(data, wcs=None, unit=None, n_iterations=3):
...
```

and then have a decorator that could make it so that ``data`` can be an ``NDData``, and ``wcs`` and ``unit`` would automatically be fetched from the ``NDData`` object, since these are valid ``NDData`` properties. The idea is that the function would only list the properties it needs and that properties that exist on NDData but can't be interpreted by the function would emit a warning so the user knows that e.g. uncertainties are not being used.

This pull request implements such a decorator, and here is an example of usage:

```
In [1]: from astropy.nddata import NDData, expand_nddata_args

In [2]: @expand_nddata_args
...: def test(data, wcs=None, unit=None):
...: print("inside test, data={0}, wcs={1}, unit={2}".format(data, wcs, unit))
...:

In [3]: import numpy as np

In [4]: data = np.array([1,2,3])

In [5]: test(data)
inside test, data=[1 2 3], wcs=None, unit=None

In [6]: d = NDData(data)

In [7]: test(d)
inside test, data=[1 2 3], wcs=None, unit=None

In [8]: d.mask = [1,0,1]

In [9]: test(d)
WARNING: The following attributes were set on the data object, but will be ignored by the function: mask [astropy.nddata.nddata_decorators]
inside test, data=[1 2 3], wcs=None, unit=None

In [10]: d.wcs = 1

In [11]: test(d)
inside test, data=[1 2 3], wcs=1, unit=None

In [12]: test(d, wcs=2)
WARNING: Property wcs has been passed explicitly and as an NDData property, using explicitly specified value [astropy.nddata.nddata_decorators]
inside test, data=[1 2 3], wcs=2, unit=None
```

The restrictions for now are:

* The NDData keywords are explicitly listed in the decorator. We could compare all kwargs to all properties, but then we start to run into the risk of accidentally matching something so we should think about this. Maybe the NDData class (and subclasses) could have an attribute that gives a list of properties that can be used in this kind of situation (or we can inspect the object specifically for properties).

* The first argument of the decorated function should be a positional ``data`` argument. I don't see why not have this restrictions since it makes a bit of the coding easier, but I'm happy to be convinced otherwise!

The function can also take arguments not specified by NDData, for example ``n_iterations`` or ``verbose`` or anything else. Those arguments are passed through untouched.

Remaining todos (if we want to go ahead with this):

* [ ] preserve signature and docstring of wrapped function
* [ ] write tests
* [ ] write proper docstring and documentation

cc @mwcraig @cdeil @perrygreenfield @crawfordsm @larrybradley @adamginsburg @Cadair (who did I miss?)