Repository: dask/dask

Issue:
DASK (latest from GIT), Pandas 0.21, (inplace parameter for rename, fillna)
inplace=True generates an error, copy is ok, 
df=df.rename(....)

```
import dask.dataframe as ddf
df=ddf.read_csv('*.csv)
df.rename(columns={'tr': 't', 'l1': 'a', 'l2': 'b'}, inplace=True)
*** TypeError: rename() got an unexpected keyword argument 'inplace'
df.fillna(0, axis=1, inplace=True)
*** TypeError: fillna() got an unexpected keyword argument 'inplace'
```





Discussion:
Thanks for the report.  FYI when making bug reports it's good to make them reproducible. Yours isn't since it depends on the presence of the CSVs in your current directory.


```python
In [24]: import pandas as pd

In [25]: import dask.dataframe as dd

In [26]: a = pd.DataFrame({"A": [1, 2]})

In [27]: b = dd.from_pandas(a, 2)

In [28]: b.rename(columns=str.lower, inplace=True)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-28-22b29884b280> in <module>()
----> 1 b.rename(columns=str.lower, inplace=True)

TypeError: rename() got an unexpected keyword argument 'inplace'

In [29]: b.fillna(0, axis=1, downcast=True)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-29-f82829535a34> in <module>()
----> 1 b.fillna(0, axis=1, downcast=True)

TypeError: fillna() got an unexpected keyword argument 'downcast'
```

`df.rename` is explicitly documented to not support `inplace` and `downcast`: http://dask.pydata.org/en/latest/dataframe-api.html#dask.dataframe.DataFrame.fillna I suspect we should add that same caveat to the notes section of `rename`.
We'll still leave this open for the documentation fix.

Resolving patch:
diff --git a/dask/compatibility.py b/dask/compatibility.py
--- a/dask/compatibility.py
+++ b/dask/compatibility.py
@@ -55,6 +55,12 @@ def apply(func, args, kwargs=None):
     def _getargspec(func):
         return inspect.getfullargspec(func)
 
+    def get_named_args(func):
+        """Get all non ``*args/**kwargs`` arguments for a function"""
+        s = inspect.signature(func)
+        return [n for n, p in s.parameters.items()
+                if p.kind == p.POSITIONAL_OR_KEYWORD]
+
     def reraise(exc, tb=None):
         if exc.__traceback__ is not tb:
             raise exc.with_traceback(tb)
@@ -93,6 +99,14 @@ def _make_reraise():
     def _getargspec(func):
         return inspect.getargspec(func)
 
+    def get_named_args(func):
+        """Get all non ``*args/**kwargs`` arguments for a function"""
+        try:
+            return getargspec(func).args
+        except TypeError as e:
+            # Be consistent with py3
+            raise ValueError(*e.args)
+
     def gzip_decompress(b):
         f = gzip.GzipFile(fileobj=BytesIO(b))
         result = f.read()
diff --git a/dask/utils.py b/dask/utils.py
--- a/dask/utils.py
+++ b/dask/utils.py
@@ -16,7 +16,7 @@
 import uuid
 from weakref import WeakValueDictionary
 
-from .compatibility import getargspec, PY3, unicode, bind_method
+from .compatibility import get_named_args, getargspec, PY3, unicode, bind_method
 from .core import get_deps
 from .context import _globals
 from .optimize import key_split    # noqa: F401
@@ -485,10 +485,10 @@ def wrapper(method):
                 doc = ''
 
             try:
-                method_args = getargspec(method).args
-                original_args = getargspec(original_method).args
+                method_args = get_named_args(method)
+                original_args = get_named_args(original_method)
                 not_supported = [m for m in original_args if m not in method_args]
-            except TypeError:
+            except ValueError:
                 not_supported = []
 
             if len(ua_args) > 0:


Test patch:
diff --git a/dask/array/tests/test_chunk.py b/dask/array/tests/test_chunk.py
--- a/dask/array/tests/test_chunk.py
+++ b/dask/array/tests/test_chunk.py
@@ -16,7 +16,6 @@ def summer(a, axis=None):
     summer_wrapped = keepdims_wrapper(summer)
 
     assert summer_wrapped != summer
-    assert summer_wrapped == keepdims_wrapper(summer_wrapped)
 
     a = np.arange(24).reshape(1, 2, 3, 4)
 
@@ -44,7 +43,6 @@ def summer(a, axis=None):
     summer_wrapped = keepdims_wrapper(summer)
 
     assert summer_wrapped != summer
-    assert summer_wrapped == keepdims_wrapper(summer_wrapped)
 
     a = np.arange(24).reshape(1, 2, 3, 4)
 
@@ -72,7 +70,6 @@ def summer(a, axis=None):
     summer_wrapped = keepdims_wrapper(summer)
 
     assert summer_wrapped != summer
-    assert summer_wrapped == keepdims_wrapper(summer_wrapped)
 
     a = np.arange(24).reshape(1, 2, 3, 4)