Repository: joblib/joblib

Issue:
v0.12: 'MemorizedResult' object has no attribute 'argument_hash'
I our test suite I was checking that caching occurred properly by checking that a ``MemorizedResult`` instance would have the same hash after two calls to ``call_and_shelve``.

Now the ``argument_hash`` attr is gone, and I replaced ``argument_hash`` with ``timestamp`` in our tests.

I was wondering if this is an oversight or if it was intended? I coudln't find anything in the changelog

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/joblib/joblib/pull/716?src=pr&el=h1) Report
> Merging [#716](https://codecov.io/gh/joblib/joblib/pull/716?src=pr&el=desc) into [master](https://codecov.io/gh/joblib/joblib/commit/6a2f9062dc01e1df013afb3886ffbaa266325764?src=pr&el=desc) will **decrease** coverage by `0.09%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/joblib/joblib/pull/716/graphs/tree.svg?width=650&height=150&src=pr&token=gA6LF5DGTW)](https://codecov.io/gh/joblib/joblib/pull/716?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##           master     #716     +/-   ##
=========================================
- Coverage   95.07%   94.98%   -0.1%     
=========================================
  Files          40       40             
  Lines        5751     5762     +11     
=========================================
+ Hits         5468     5473      +5     
- Misses        283      289      +6
```


| [Impacted Files](https://codecov.io/gh/joblib/joblib/pull/716?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [joblib/memory.py](https://codecov.io/gh/joblib/joblib/pull/716/diff?src=pr&el=tree#diff-am9ibGliL21lbW9yeS5weQ==) | `95.08% <100%> (-0.25%)` | :arrow_down: |
| [joblib/test/test\_memory.py](https://codecov.io/gh/joblib/joblib/pull/716/diff?src=pr&el=tree#diff-am9ibGliL3Rlc3QvdGVzdF9tZW1vcnkucHk=) | `97.49% <100%> (+0.03%)` | :arrow_up: |
| [joblib/testing.py](https://codecov.io/gh/joblib/joblib/pull/716/diff?src=pr&el=tree#diff-am9ibGliL3Rlc3RpbmcucHk=) | `87.5% <0%> (-7.5%)` | :arrow_down: |
| [joblib/func\_inspect.py](https://codecov.io/gh/joblib/joblib/pull/716/diff?src=pr&el=tree#diff-am9ibGliL2Z1bmNfaW5zcGVjdC5weQ==) | `91.42% <0%> (-3.43%)` | :arrow_down: |
| [joblib/disk.py](https://codecov.io/gh/joblib/joblib/pull/716/diff?src=pr&el=tree#diff-am9ibGliL2Rpc2sucHk=) | `88.33% <0%> (+6.66%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/joblib/joblib/pull/716?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/joblib/joblib/pull/716?src=pr&el=footer). Last update [6a2f906...d449768](https://codecov.io/gh/joblib/joblib/pull/716?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index cb3bf00..3449861 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,6 +1,19 @@
 Latest changes
 ===============
 
+Release 0.12.1
+--------------
+
+Thomas Moreau
+
+    Make sure that any exception triggered when serializing jobs in the queue
+    will be wrapped as a PicklingError as in past versions of joblib.
+
+Noam Hershtig
+
+    Fix kwonlydefaults key error in filter_args (#715)
+
+
 Release 0.12
 ------------
 
diff --git a/doc/memory.rst b/doc/memory.rst
index 004a4e1..0850550 100644
--- a/doc/memory.rst
+++ b/doc/memory.rst
@@ -206,7 +206,7 @@ Getting a reference to the cache can be done using the
     >>> result = g.call_and_shelve(4)
     A long-running calculation, with parameter 4
     >>> result  #doctest: +ELLIPSIS
-    MemorizedResult(location="...", func="...g...", argument_hash="...")
+    MemorizedResult(location="...", func="...g...", args_id="...")
 
 Once computed, the output of `g` is stored on disk, and deleted from
 memory. Reading the associated value can then be performed with the
diff --git a/joblib/__init__.py b/joblib/__init__.py
index 7440f36..10ff437 100644
--- a/joblib/__init__.py
+++ b/joblib/__init__.py
@@ -106,7 +106,7 @@ Main features
 # Dev branch marker is: 'X.Y.dev' or 'X.Y.devN' where N is an integer.
 # 'X.Y.dev0' is the canonical version of 'X.Y.dev'
 #
-__version__ = '0.12.0'
+__version__ = '0.12.1'
 
 
 from .memory import Memory, MemorizedResult, register_store_backend
diff --git a/joblib/func_inspect.py b/joblib/func_inspect.py
index f8520c9..e9320b4 100644
--- a/joblib/func_inspect.py
+++ b/joblib/func_inspect.py
@@ -240,8 +240,10 @@ def filter_args(func, ignore_lst, args=(), kwargs=dict()):
     arg_spec = getfullargspec(func)
     arg_names = arg_spec.args + arg_spec.kwonlyargs
     arg_defaults = arg_spec.defaults or ()
-    arg_defaults = arg_defaults + tuple(arg_spec.kwonlydefaults[k]
-                                        for k in arg_spec.kwonlyargs)
+    if arg_spec.kwonlydefaults:
+        arg_defaults = arg_defaults + tuple(arg_spec.kwonlydefaults[k]
+                                            for k in arg_spec.kwonlyargs
+                                            if k in arg_spec.kwonlydefaults)
     arg_varargs = arg_spec.varargs
     arg_varkw = arg_spec.varkw
 
diff --git a/joblib/memory.py b/joblib/memory.py
index 0c0e15d..91204af 100644
--- a/joblib/memory.py
+++ b/joblib/memory.py
@@ -224,6 +224,15 @@ class MemorizedResult(Logger):
         self.verbose = verbose
         self.timestamp = timestamp
 
+    @property
+    def argument_hash(self):
+        warnings.warn(
+            "The 'argument_hash' attribute has been deprecated in version "
+            "0.12 and will be removed in version 0.14.\n"
+            "Use `args_id` attribute instead.",
+            DeprecationWarning, stacklevel=2)
+        return self.args_id
+
     def get(self):
         """Read value from cache and return it."""
         if self.verbose:
@@ -241,13 +250,12 @@ class MemorizedResult(Logger):
 
     def __repr__(self):
         return ('{class_name}(location="{location}", func="{func}", '
-                'argument_hash="{argument_hash}")'
+                'args_id="{args_id}")'
                 .format(class_name=self.__class__.__name__,
                         location=self.store_backend,
                         func=self.func,
-                        argument_hash=self.args_id
+                        args_id=self.args_id
                         ))
-
     def __reduce__(self):
         return (self.__class__,
                 (self.store_backend, self.func, self.args_id),
@@ -824,8 +832,8 @@ class Memory(Logger):
             warnings.warn(
                 "The 'cachedir' parameter has been deprecated in version "
                 "0.12 and will be removed in version 0.14.\n"
-                'You provided "cachedir={!r}", '
-                'use "location={!r}" instead.'.format(cachedir, location),
+                'You provided "cachedir={0!r}", '
+                'use "location={0!r}" instead.'.format(cachedir),
                 DeprecationWarning, stacklevel=2)
             location = cachedir
 


Test patch:
diff --git a/joblib/test/test_func_inspect.py b/joblib/test/test_func_inspect.py
index 1d094b0..baccecc 100644
--- a/joblib/test/test_func_inspect.py
+++ b/joblib/test/test_func_inspect.py
@@ -99,11 +99,25 @@ def test_filter_varargs(func, args, filtered_args):
     assert filter_args(func, *args) == filtered_args
 
 
+test_filter_kwargs_extra_params = []
+if PY3_OR_LATER:
+    m1 = m2 = None
+    # The following statements raise SyntaxError in python 2
+    # because kwargonly is not supported
+    exec("def m1(x, *, y): pass")
+    exec("def m2(x, *, y, z=3): pass")
+    test_filter_kwargs_extra_params.extend([
+        (m1, [[], (1,), {'y': 2}], {'x': 1, 'y': 2}),
+        (m2, [[], (1,), {'y': 2}], {'x': 1, 'y': 2, 'z': 3})
+    ])
+
+
 @parametrize('func,args,filtered_args',
              [(k, [[], (1, 2), {'ee': 2}],
                {'*': [1, 2], '**': {'ee': 2}}),
               (k, [[], (3, 4)],
-               {'*': [3, 4], '**': {}})])
+               {'*': [3, 4], '**': {}})] +
+             test_filter_kwargs_extra_params)
 def test_filter_kwargs(func, args, filtered_args):
     assert filter_args(func, *args) == filtered_args
 
diff --git a/joblib/test/test_memory.py b/joblib/test/test_memory.py
index e795e71..751e29a 100644
--- a/joblib/test/test_memory.py
+++ b/joblib/test/test_memory.py
@@ -451,6 +451,19 @@ def test_call_and_shelve(tmpdir):
         result.clear()  # Do nothing if there is no cache.
 
 
+def test_call_and_shelve_argument_hash(tmpdir):
+    # Verify that a warning is raised when accessing arguments_hash
+    # attribute from MemorizedResult
+    func = Memory(location=tmpdir.strpath, verbose=0).cache(f)
+    result = func.call_and_shelve(2)
+    assert isinstance(result, MemorizedResult)
+    with warns(DeprecationWarning) as w:
+        assert result.argument_hash == result.args_id
+    assert len(w) == 1
+    assert "The 'argument_hash' attribute has been deprecated" \
+        in str(w[-1].message)
+
+
 def test_memorized_pickling(tmpdir):
     for func in (MemorizedFunc(f, tmpdir.strpath), NotMemorizedFunc(f)):
         filename = tmpdir.join('pickling_test.dat').strpath