Repository: nilearn/nilearn

Issue:
check_embedded_nifti_masker assumes estimator has attributes set
**Disclaimer:** I know `check_embedded_nifti_masker` is implicitly meant to be a low-level function, etc., etc.

```
Traceback (most recent call last):
  File "toto.py", line 13, in <module>
    Toto(memory=None).fit()
  File "toto.py", line 10, in fit
    self.masker_ = check_embedded_nifti_masker(self)
  File "/home/elvis/.local/lib/python2.7/site-packages/nilearn-0.2.3-py2.7.egg/nilearn/input_data/masker_validation.py", line 53, in check_embedded_nifti_masker
    new_masker_params['memory_level'] = max(0, estimator.memory_level - 1)
AttributeError: 'Toto' object has no attribute 'memory_level'
```

Reproduce with:

```
from nilearn.input_data.masker_validation import check_embedded_nifti_masker


class Toto(object):
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

    def fit(self, *args, **kwargs):
        self.masker_ = check_embedded_nifti_masker(self)


Toto(memory=None).fit()
```

check_embedded_nifti_masker assumes estimator has attributes set
**Disclaimer:** I know `check_embedded_nifti_masker` is implicitly meant to be a low-level function, etc., etc.

```
Traceback (most recent call last):
  File "toto.py", line 13, in <module>
    Toto(memory=None).fit()
  File "toto.py", line 10, in fit
    self.masker_ = check_embedded_nifti_masker(self)
  File "/home/elvis/.local/lib/python2.7/site-packages/nilearn-0.2.3-py2.7.egg/nilearn/input_data/masker_validation.py", line 53, in check_embedded_nifti_masker
    new_masker_params['memory_level'] = max(0, estimator.memory_level - 1)
AttributeError: 'Toto' object has no attribute 'memory_level'
```

Reproduce with:

```
from nilearn.input_data.masker_validation import check_embedded_nifti_masker


class Toto(object):
    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

    def fit(self, *args, **kwargs):
        self.masker_ = check_embedded_nifti_masker(self)


Toto(memory=None).fit()
```



Discussion:
If I am not wrong, this function is designed only for NiftiMasker or MultiNiftiMasker and arguments you have provided are related to either of the object.

If I understand correctly, one can fix this by raising a meaningful error message if user did not provide a masker related object ? Any opinions ?

What Evlis says is that we should test if the parameter is a masker and fail with a more meaningful error message if not.

If I am not wrong, this function is designed only for NiftiMasker or MultiNiftiMasker and arguments you have provided are related to either of the object.

If I understand correctly, one can fix this by raising a meaningful error message if user did not provide a masker related object ? Any opinions ?

What Evlis says is that we should test if the parameter is a masker and fail with a more meaningful error message if not.


Resolving patch:
diff --git a/nilearn/input_data/masker_validation.py b/nilearn/input_data/masker_validation.py
--- a/nilearn/input_data/masker_validation.py
+++ b/nilearn/input_data/masker_validation.py
@@ -1,6 +1,7 @@
 import warnings
 
 import numpy as np
+from string import Template
 
 from .._utils.class_inspect import get_params
 from .._utils.cache_mixin import _check_memory
@@ -52,9 +53,30 @@ def check_embedded_nifti_masker(estimator, multi_subject=True):
     if multi_subject and hasattr(estimator, 'n_jobs'):
         # For MultiNiftiMasker only
         new_masker_params['n_jobs'] = estimator.n_jobs
-    new_masker_params['memory'] = _check_memory(estimator.memory)
-    new_masker_params['memory_level'] = max(0, estimator.memory_level - 1)
-    new_masker_params['verbose'] = estimator.verbose
+
+    warning_msg = Template("Provided estimator has no $attribute attribute set."
+                           "Setting $attribute to $default_value by default.")
+
+    if hasattr(estimator, 'memory'):
+        new_masker_params['memory'] = _check_memory(estimator.memory)
+    else:
+        warnings.warn(warning_msg.substitute(attribute='memory',
+                                             default_value='Memory(location=None)'))
+        new_masker_params['memory'] = _check_memory(None)
+
+    if hasattr(estimator, 'memory_level'):
+        new_masker_params['memory_level'] = max(0, estimator.memory_level - 1)
+    else:
+        warnings.warn(warning_msg.substitute(attribute='memory_level',
+                                             default_value='0'))
+        new_masker_params['memory_level'] = 0
+
+    if hasattr(estimator, 'verbose'):
+        new_masker_params['verbose'] = estimator.verbose
+    else:
+        warnings.warn(warning_msg.substitute(attribute='verbose',
+                                             default_value='0'))
+        new_masker_params['verbose'] = 0
 
     # Raising warning if masker override parameters
     conflict_string = ""


Test patch:
diff --git a/nilearn/input_data/tests/test_masker_validation.py b/nilearn/input_data/tests/test_masker_validation.py
--- a/nilearn/input_data/tests/test_masker_validation.py
+++ b/nilearn/input_data/tests/test_masker_validation.py
@@ -37,6 +37,29 @@ def __init__(self, mask=None, smoothing_fwhm=None,
         self.verbose = verbose
         self.dummy = dummy
 
+class DummyEstimator(object):
+
+    def __init__(self, **kwargs):
+        for k, v in kwargs.items():
+            setattr(self, k, v)
+
+    def fit(self, *args, **kwargs):
+        self.masker = check_embedded_nifti_masker(self)
+
+def test_check_embedded_nifti_masker_defaults():
+    dummy = DummyEstimator(memory=None, memory_level=1)
+    with pytest.warns(Warning, match="Provided estimator has no verbose attribute set."):
+        dummy.fit()
+    assert dummy.masker.memory_level == 0
+    assert dummy.masker.verbose == 0
+    dummy = DummyEstimator(verbose=1)
+    with pytest.warns(Warning, match="Provided estimator has no memory attribute set."):
+        dummy.fit()
+    assert isinstance(dummy.masker.memory, Memory)
+    assert dummy.masker.memory.location is None
+    assert dummy.masker.memory_level == 0
+    assert dummy.masker.verbose == 1
+
 
 def test_check_embedded_nifti_masker():
     owner = OwningClass()