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

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

```diff
@@            Coverage Diff             @@
##           master     #765      +/-   ##
==========================================
- Coverage   95.42%   95.31%   -0.11%     
==========================================
  Files          44       44              
  Lines        6224     6235      +11     
==========================================
+ Hits         5939     5943       +4     
- Misses        285      292       +7
```


| [Impacted Files](https://codecov.io/gh/joblib/joblib/pull/765?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [joblib/\_store\_backends.py](https://codecov.io/gh/joblib/joblib/pull/765/diff?src=pr&el=tree#diff-am9ibGliL19zdG9yZV9iYWNrZW5kcy5weQ==) | `90.57% <100%> (+0.62%)` | :arrow_up: |
| [joblib/test/test\_memory.py](https://codecov.io/gh/joblib/joblib/pull/765/diff?src=pr&el=tree#diff-am9ibGliL3Rlc3QvdGVzdF9tZW1vcnkucHk=) | `98.11% <100%> (+0.02%)` | :arrow_up: |
| [joblib/disk.py](https://codecov.io/gh/joblib/joblib/pull/765/diff?src=pr&el=tree#diff-am9ibGliL2Rpc2sucHk=) | `81.66% <0%> (-6.67%)` | :arrow_down: |
| [joblib/test/test\_dask.py](https://codecov.io/gh/joblib/joblib/pull/765/diff?src=pr&el=tree#diff-am9ibGliL3Rlc3QvdGVzdF9kYXNrLnB5) | `96.22% <0%> (-1.89%)` | :arrow_down: |
| [joblib/\_parallel\_backends.py](https://codecov.io/gh/joblib/joblib/pull/765/diff?src=pr&el=tree#diff-am9ibGliL19wYXJhbGxlbF9iYWNrZW5kcy5weQ==) | `95.6% <0%> (-0.41%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/joblib/joblib/pull/765?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/765?src=pr&el=footer). Last update [f64e1b8...bf5f24e](https://codecov.io/gh/joblib/joblib/pull/765?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

lesteve: Can you check that self.store_backend is not used in other classes `__repr__`. I seem to remember it is.
aabadie: > Can you check that self.store_backend is not used in other classes __repr__. I seem to remember it is.

I just did and you are right it was used in `MemorizedResult` and `MemorizedFunc` classes. It's fixed now.
aabadie: @ogrisel, is it ok to merge ?
diff --git a/README.rst b/README.rst
index 472a5fd..1ed63ef 100644
--- a/README.rst
+++ b/README.rst
@@ -58,6 +58,8 @@ Dependencies
 - Joblib has an optional dependency on
   `python-lz4 <https://pypi.python.org/pypi/lz4>`_ as a faster alternative to
   zlib and gzip for compressed serialization.
+- Joblib has an optional dependency on psutil to mitigate memory leaks in
+  parallel worker processes.
 - Some examples require external dependencies such as pandas. See the
   instructions in the `Building the docs`_ section for details.
 
diff --git a/joblib/_store_backends.py b/joblib/_store_backends.py
index d48edcd..9196f0a 100644
--- a/joblib/_store_backends.py
+++ b/joblib/_store_backends.py
@@ -35,6 +35,8 @@ class StoreBackendBase(with_metaclass(ABCMeta)):
     """Helper Abstract Base Class which defines all methods that
        a StorageBackend must implement."""
 
+    location = None
+
     @abstractmethod
     def _open_item(self, f, mode):
         """Opens an item on the store and return a file-like object.
@@ -327,7 +329,8 @@ class StoreBackendMixin(object):
 
     def __repr__(self):
         """Printable representation of the store location."""
-        return self.location
+        return '{class_name}(location="{location}")'.format(
+            class_name=self.__class__.__name__, location=self.location)
 
 
 class FileSystemStoreBackend(StoreBackendBase, StoreBackendMixin):
diff --git a/joblib/memory.py b/joblib/memory.py
index 491238c..5ae6940 100644
--- a/joblib/memory.py
+++ b/joblib/memory.py
@@ -258,7 +258,7 @@ class MemorizedResult(Logger):
         return ('{class_name}(location="{location}", func="{func}", '
                 'args_id="{args_id}")'
                 .format(class_name=self.__class__.__name__,
-                        location=self.store_backend,
+                        location=self.store_backend.location,
                         func=self.func,
                         args_id=self.args_id
                         ))
@@ -769,9 +769,10 @@ class MemorizedFunc(Logger):
     # ------------------------------------------------------------------------
 
     def __repr__(self):
-        return ("{0}(func={1}, location={2})".format(self.__class__.__name__,
-                                                     self.func,
-                                                     self.store_backend,))
+        return '{class_name}(func={func}, location={location})'.format(
+            class_name=self.__class__.__name__,
+            func=self.func,
+            location=self.store_backend.location,)
 
 
 ###############################################################################
@@ -963,9 +964,10 @@ class Memory(Logger):
     # ------------------------------------------------------------------------
 
     def __repr__(self):
-        return '{0}(location={1})'.format(
-            self.__class__.__name__, (repr(None) if self.store_backend is None
-                                      else repr(self.store_backend)))
+        return '{class_name}(location={location})'.format(
+            class_name=self.__class__.__name__,
+            location=(None if self.store_backend is None
+                      else self.store_backend.location))
 
     def __getstate__(self):
         """ We don't store the timestamp when pickling, to avoid the hash

FileSystemStoreBackend.__repr__ should not return a string
Something that tripped me up when working on https://github.com/joblib/joblib/pull/746, is that `FileSystemStoreBackend.__repr__` makes it look like a string. I could not find a good reason for this. Do you remember something about this @aabadie?

Here is a snippet to show what I mean:
```
In [1]: from joblib import Memory

In [2]: mem = Memory('/tmp/test')

In [3]: mem.store_backend
Out[3]: /tmp/test/joblib
```

When I was debugging I lost at least one hour until I thought of doing `type(mem.store_backend)` ...

Also slightly less important but:
```py
from joblib._store_backends import FileSystemStoreBackend
backend = FileSystemStoreBackend()
backend  # Exception in __repr__ AttributeError: 'FileSystemStoreBackend' object has no attribute 'location'
```
diff --git a/joblib/test/test_memory.py b/joblib/test/test_memory.py
index b3ac081..2f0c1b7 100644
--- a/joblib/test/test_memory.py
+++ b/joblib/test/test_memory.py
@@ -24,7 +24,7 @@ from joblib.memory import register_store_backend, _STORE_BACKENDS
 from joblib.memory import _build_func_identifier, _store_backend_factory
 from joblib.memory import JobLibCollisionWarning
 from joblib.parallel import Parallel, delayed
-from joblib._store_backends import StoreBackendBase
+from joblib._store_backends import StoreBackendBase, FileSystemStoreBackend
 from joblib.test.common import with_numpy, np
 from joblib.test.common import with_multiprocessing
 from joblib.testing import parametrize, raises, warns
@@ -989,6 +989,55 @@ def test_dummy_store_backend():
     assert isinstance(backend_obj, DummyStoreBackend)
 
 
+def test_filesystem_store_backend_repr(tmpdir):
+    # Verify string representation of a filesystem store backend.
+
+    repr_pattern = 'FileSystemStoreBackend(location="{location}")'
+    backend = FileSystemStoreBackend()
+    assert backend.location is None
+
+    repr(backend)  # Should not raise an exception
+
+    assert str(backend) == repr_pattern.format(location=None)
+
+    # backend location is passed explicitely via the configure method (called
+    # by the internal _store_backend_factory function)
+    backend.configure(tmpdir.strpath)
+
+    assert str(backend) == repr_pattern.format(location=tmpdir.strpath)
+
+    repr(backend)  # Should not raise an exception
+
+
+def test_memory_objects_repr(tmpdir):
+    # Verify printable reprs of MemorizedResult, MemorizedFunc and Memory.
+
+    def my_func(a, b):
+        return a + b
+
+    memory = Memory(location=tmpdir.strpath, verbose=0)
+    memorized_func = memory.cache(my_func)
+
+    memorized_func_repr = 'MemorizedFunc(func={func}, location={location})'
+
+    assert str(memorized_func) == memorized_func_repr.format(
+        func=my_func,
+        location=memory.store_backend.location)
+
+    memorized_result = memorized_func.call_and_shelve(42, 42)
+
+    memorized_result_repr = ('MemorizedResult(location="{location}", '
+                             'func="{func}", args_id="{args_id}")')
+
+    assert str(memorized_result) == memorized_result_repr.format(
+        location=memory.store_backend.location,
+        func=memorized_result.func_id,
+        args_id=memorized_result.args_id)
+
+    assert str(memory) == 'Memory(location={location})'.format(
+        location=memory.store_backend.location)
+
+
 def test_memorized_result_pickle(tmpdir):
     # Verify a MemoryResult object can be pickled/depickled. Non regression
     # test introduced following issue

pip install -e .
parse_log_pytest
pytest
pytest
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
certifi==2021.5.30
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1631916693255/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
-e git+https://github.com/joblib/joblib.git@f64e1b8cf286c02d2782c6dd5bf7a42e0518c51d#egg=joblib
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: joblib
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib-metadata=4.8.1=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
prefix: /opt/conda/envs/joblib

joblib/test/test_memory.py::test_filesystem_store_backend_repr
joblib/test/test_memory.py::test_memory_integration joblib/test/test_memory.py::test_no_memory joblib/test/test_memory.py::test_memory_kwarg joblib/test/test_memory.py::test_memory_lambda joblib/test/test_memory.py::test_memory_name_collision joblib/test/test_memory.py::test_memory_warning_lambda_collisions joblib/test/test_memory.py::test_memory_warning_collision_detection joblib/test/test_memory.py::test_memory_partial joblib/test/test_memory.py::test_memory_eval joblib/test/test_memory.py::test_argument_change joblib/test/test_memory.py::test_memory_exception joblib/test/test_memory.py::test_memory_ignore joblib/test/test_memory.py::test_memory_args_as_kwargs joblib/test/test_memory.py::test_partial_decoration[ignore0-100-r] joblib/test/test_memory.py::test_partial_decoration[ignore1-10-None] joblib/test/test_memory.py::test_func_dir joblib/test/test_memory.py::test_persistence joblib/test/test_memory.py::test_call_and_shelve joblib/test/test_memory.py::test_call_and_shelve_argument_hash joblib/test/test_memory.py::test_memorized_pickling joblib/test/test_memory.py::test_memorized_repr joblib/test/test_memory.py::test_memory_file_modification joblib/test/test_memory.py::test_memory_in_memory_function_code_change joblib/test/test_memory.py::test_clear_memory_with_none_location joblib/test/test_memory.py::test_memory_func_with_kwonly_args joblib/test/test_memory.py::test_memory_func_with_signature joblib/test/test_memory.py::test__get_items joblib/test/test_memory.py::test__get_items_to_delete joblib/test/test_memory.py::test_memory_reduce_size joblib/test/test_memory.py::test_memory_clear joblib/test/test_memory.py::test_cached_function_race_condition_when_persisting_output joblib/test/test_memory.py::test_cached_function_race_condition_when_persisting_output_2 joblib/test/test_memory.py::test_memory_recomputes_after_an_error_why_loading_results joblib/test/test_memory.py::test_deprecated_cachedir_behaviour joblib/test/test_memory.py::test_register_invalid_store_backends_key[None] joblib/test/test_memory.py::test_register_invalid_store_backends_key[invalid_prefix1] joblib/test/test_memory.py::test_register_invalid_store_backends_key[invalid_prefix2] joblib/test/test_memory.py::test_register_invalid_store_backends_object joblib/test/test_memory.py::test_memory_default_store_backend joblib/test/test_memory.py::test_instanciate_incomplete_store_backend joblib/test/test_memory.py::test_dummy_store_backend joblib/test/test_memory.py::test_memory_objects_repr joblib/test/test_memory.py::test_memorized_result_pickle joblib/test/test_memory.py::test_memory_pickle_dump_load[memory_kwargs0] joblib/test/test_memory.py::test_memory_pickle_dump_load[memory_kwargs1]
README.rst joblib/memory.py joblib/_store_backends.py
README.rst joblib/memory.py joblib/_store_backends.py