8000 [MRG] Fix DeprecationWarning due to collections.abc in Python 3.7 by rth · Pull Request #11431 · scikit-learn/scikit-learn · GitHub
[go: up one dir, main page]

Skip to content

[MRG] Fix DeprecationWarning due to collections.abc in Python 3.7 #11431

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jul 14, 2018

Conversation

rth
Copy link
Member
@rth rth commented Jul 4, 2018

Closes #11121

This PR removes the deprecation warning about ABC being moved from collections to collections.abc when importing scikit-learn in Python 3.7.

In the end, I put collections.abc.{Sequence, Iterable, Mapping, Sized} in the namespace of sklearn.utils.fixes. This was the simplest way I could find, and while it has the drawback of obfuscating the real module name, other approached appeared more problematic and a similar approach is currently used e.g. for utils.fixes.signature which is an alias for inspect.signature.

We can't just patch six with benjaminp/six#241, because sklearn uses six from 5 years ago, which would need updating and I'm not sure if it could have side effects (e.g. for pickling backward compatibility etc).

Edit: This adds a test checking that generally no warnings are raised when importing scikit-learn top-level modules.

# Test that importing scikit-learn doesn't raise any warnings.

message = subprocess.check_output(['python', '-Wdefault',
'-c', 'import sklearn'],
Copy link
Member Author
@rth rth Jul 4, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure there is a better way of testing this? Trying to run the import in a separate process with multiprocessing but I couldn't see the deprecation warning. Using importlib.reload + "always" warning filter also didn't work. Besides I wasn't sure how it might interact with pytest warning capture, so using subprocess seems easier to isolate the environment.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this really fail at master? Surely sklearn/__init__.py is light enough that no warning is triggered...?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does fail on master with Python 3.7 without this PR as described in the original issue #11121 (comment)

sklearn/__init__.py imports base.py which imports sklearn/utils/__init__.py which imports collections.abc.Sequence. There is second place it is raised from the __init__ I think as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really see the point in testing the import of sklearn, and not testing the import of every module for either an internal DeprecationWarning or no warning.

@rth rth changed the title [MRG] Fix DeprecationWarning due to collections.abc with Python 3.7 [MRG] Fix DeprecationWarning due to collections.abc in Python 3.7 Jul 4, 2018
@sklearn-lgtm
Copy link

This pull request introduces 4 alerts when merging 3018864 into 6f9a8df - view on LGTM.com

new alerts:

  • 4 for Unused import

Comment posted by LGTM.com

@rth
Copy link
Member Author
rth commented Jul 5, 2018

This pull request introduces 4 alerts

These are false positive for unused imports in fixes..

@rth
Copy link
Member Author
rth commented Jul 5, 2018

I don't really see the point in testing the import of sklearn, and not testing the import of every module for either an internal DeprecationWarning or no warning.

Good point. The last commit tests that no warning is raised at import by any of the top level modules.

Another point I was concerned about is that this is quite easily breakable. For instance, future numpy release in 1 year might still work with 0.20 but raise some DeprecationWarning, which will break this test. So I have skipped this test at failure, while printing an informative skipped message. For instance, introducing a DeprecationWarning in __init__.py will result in,

pytest sklearn/tests/test_init.py -v        
==================================================================== test session starts ====================================================================
platform linux -- Python 3.6.5, pytest-3.5.1, py-1.5.3, pluggy-0.6.0 -- /home/rth/.miniconda3/envs/sklearn-dev/bin/python
cachedir: .pytest_cache
rootdir: /home/rth/src/scikit-learn, inifile: setup.cfg
collected 2 items                                                                                                                                           

sklearn/tests/test_init.py::test_import_skl PASSED
sklearn/tests/test_init.py::test_import_sklearn_no_warnings SKIPPED
================================================================== short test summary info ==================================================================
SKIP [1] /home/rth/src/scikit-learn/sklearn/tests/test_init.py:60: soft-failed test_import_sklearn_no_warnings.
 assert 'Warning' not in '  return f(*args, **k... **kwds)\n  import imp'
  'Warning' is contained here:
      return f(*args, **kwds)
    /home/rth/src/scikit-learn/sklearn/__init__.py:82: DeprecationWarning: some error
  ?                                                               +++++++
      warnings.warn('some error', DeprecationWarning)
      return f(*args, **kwds)
      import imp

============================================================ 1 passed, 1 skipped in 1.06 seconds ============================================================

@jnothman
Copy link
Member
jnothman commented Jul 5, 2018 via email

@sklearn-lgtm
Copy link

This pull request introduces 4 alerts when merging 128d460 into 6f9a8df - view on LGTM.com

new alerts:

  • 4 for Unused import

Comment posted by LGTM.com

@rth
Copy link
Member Author
rth commented Jul 5, 2018

why don't you just run it as a separate ci script if you want it to happen
in ci but not at home?

I want this to be tested both in CI and when running tests locally. I just don't want this be reported as a critical test failure, when it does fail (e.g. with outdated or future versions of dependencies).

8000

@jnothman
Copy link
Member
jnothman commented Jul 5, 2018 via email

@rth
Copy link
Member Author
rth commented Jul 5, 2018

The error message will still be visible in the "short test summary info" pytest section where skipped tests are listed.

To give you an example, in https://travis-ci.org/scikit-learn/scikit-learn/builds/400205299

this tests used to fail,

  • for Py2.7 due to "ModuleDeprecationWarning: The oldnumeric module will be dropped in Numpy 1.9"
  • for Py3.4 due to "FutureWarning: numpy not_equal will not check object identity in the future."

these are now handled as special case and are ignored, but there may be other combinations of systems / dependencies would produce other warnings.

The point I think is that we don't want to be in a situation where this test makes the tests suite fail during downstream packaging of scikit-learn (e.g. for debian, using PyPy etc) due to some peculiarities of the environment. So IMO this test is inherently not robust enough to be included as a mandatory test, but at the same time it's useful to know information as currently we have no mechanism to detect issues like #11121. This provides such mechanism with a test that is allowed to fail.

@rth
Copy link
Member Author
rth commented Jul 5, 2018

Also it's not ideal to make it mandatory in CI and optional locally (for instance looking with if 'CI' in os.environ), because other downstream packaging may also happen in CI. I'm not so keen on a separate script unrelated to the test suite..

Unless you see a better approach?

@rth
Copy link
Member Author
rth commented Jul 10, 2018

Is there anything else I can do to make a more convincing case for this PR @jnothman ? I can remove the added test if you are not enthusiastic about it, but then this deprecation warning filter won't be tested.

I think it might be worth backporting this fix to 0.19.2.

Maybe @lesteve would have an opinion about this PR as well.

Copy link
Member
@jnothman jnothman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a pity we can't just rely on six, but this LGTM.

@jnothman
Copy link
Member
jnothman commented Jul 10, 2018 via email

# Test that importing scikit-learn main modules doesn't raise any warnings.

try:
pkgs = pkgutil.iter_modules(path=sklearn.__path__, prefix='sklearn.')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait what happens if a submodule is deprecated? then this fails, right?

@amueller
Copy link
Member

lgtm apart from the test which is a bit confusing to me.

@amueller
Copy link
Member

so we backport to 0.19.2, release, and then remove this again in 0.21-dev?

@amueller
Copy link
Member

hm these are public so we would need to deprecate them to remove them again? or we make fixes private? :-/ can we import as _Thing so it's explicit they are private maybe?

@rth
Copy link
Member Author
rth commented Jul 12, 2018

Thanks for the review!

wait what happens if a submodule is deprecated? then this fails, right?

Yes, it means that when new modules are deprecated, they would need to be explicitly excluded from this check.

so we backport to 0.19.2, release, and then remove this again in 0.21-dev?

If py2.7 support is dropped in 0.21-dev, we could indeed remove this fix, and just use collections.abc in imports.

can we import as _Thing so it's explicit they are private maybe?

Good point. Will make them private in fixes (e.g. from collection.abc import Sequence as _Sequence) then re-import with a public name, from .fixes import _Sequence as Sequence) to reduce the amount of code changes.

lgtm apart from the test which is a bit confusing to me.

I agree the test is a bit convoluted but I couldn't find a simpler solution. What should I do then: remove it altogether, considering that the fix will not be needed in 0.21?

@sklearn-lgtm
Copy link

This pull request introduces 8 alerts when merging c9dc88f into 6f9a8df - view on LGTM.com

new alerts:

  • 8 for Unused import

Comment posted by LGTM.com

for _, modname, _ in pkgs
if not modname.startswith('_')])

message = subprocess.check_output(['python', '-Wdefault',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should probably do this in tests/test_init.py where we check that importing sklearn raises no warning.

message = message.decode("utf-8")
message = '\n'.join([line for line in message.splitlines()
if not ( # ignore ImportWarning
"ImportWarning" in line or
Copy link
F438 Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is that?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my case, I get,

$ python -Wdefault -c "import sklearn"                                                        [collections-abc-py37] 
/home/rth/.miniconda3/envs/sklearn-dev/lib/python3.6/importlib/_bootstrap.py:219: ImportWarning: can't resolve package from __spec__ or __package__, falling back on __name__ and __path__
  return f(*args, **kwds)
/home/rth/.miniconda3/envs/sklearn-dev/lib/python3.6/importlib/_bootstrap.py:219: ImportWarning: can't resolve package from __spec__ or __package__, falling back on __name__ and __path__
  return f(*args, **kwds)

so this ignores these ImportWarnings

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you know what that means? And does that mean we want to ignore all import warnings?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a related discussion in astropy/astropy#6025 suggesting some of it could come from Cython. In my case, it seems to be due to importing a pyx file in MKL,

PYTHONWARNINGS='error::ImportWarning' python -c "import sklearn"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/home/rth/src/scikit-learn/sklearn/__init__.py", line 64, in <module>
    from .base import clone
  File "/home/rth/src/scikit-learn/sklearn/base.py", line 10, in <module>
    import numpy as np
  File "/home/rth/.miniconda3/envs/sklearn-dev/lib/python3.6/site-packages/numpy/__init__.py", line 180, in <module>
    from . import fft
  File "/home/rth/.miniconda3/envs/sklearn-dev/lib/python3.6/site-packages/numpy/fft/__init__.py", line 14, in <module>
    import mkl_fft._numpy_fft as _nfft
  File "/home/rth/.miniconda3/envs/sklearn-dev/lib/python3.6/site-packages/mkl_fft/__init__.py", line 27, in <module>
    from ._pydfti import (fft, ifft, fft2, ifft2, fftn, ifftn, rfft, irfft,
  File "mkl_fft/_pydfti.pyx", line 27, in init mkl_fft._pydfti
ImportWarning: can't resolve package from __spec__ or __package__, falling back on __name__ and __path__

in any case it seems to be upstream issues, that will not be seen by users (as ImportWarning is ignored by default), I'm not sure it's worth the effort investigating further.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair, maybe add a comment to this effect?

# ignore DeprecationWarnings due to
10000 # numpy.oldnumeric
"oldnumeric" in line or
# ignore FutureWarnings
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? which ones? from sklearn or external? If internal, maybe we should have our own subclass?

@amueller
Copy link
Member

seems good to me.

@sklearn-lgtm
Copy link

This pull request introduces 8 alerts when merging a2f208d into 6f9a8df - view on LGTM.com

new alerts:

  • 8 for Unused import

Comment posted by LGTM.com

@amueller amueller merged commit c09352c into scikit-learn:master Jul 14, 2018
wdevazelhes pushed a commit to wdevazelhes/scikit-learn that referenced this pull request Jul 15, 2018
…ikit-learn#11431)

Closes scikit-learn#11121

This PR removes the deprecation warning about ABC being moved from `collections` to `collections.abc` when importing scikit-learn in Python 3.7.

In the end, I put `collections.abc.{Sequence, Iterable, Mapping, Sized}` in the namespace of `sklearn.utils.fixes`. This was the simplest way I could find, and while it has the drawback of obfuscating the real module name, other approached appeared more problematic and a similar approach is currently used e.g. for `utils.fixes.signature` which is an alias for `inspect.signature`.

We can't just patch six with benjaminp/six#241, because sklearn uses six from 5 years ago, which would  need updating and I'm not sure if it could have side effects (e.g. for pickling backward compatibility etc).

**Edit**: This adds a test checking that generally no warnings are raised when importing scikit-learn top-level modules.
@rth rth deleted the collections-abc-py37 branch October 2, 2018 10:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

DeprecationWarning: Using or importing the ABCs from 'collections' in Python 3.7
4 participants
0