Open jayvdb opened 5 years ago
unittest.mock.patch
gives AttributeError on all versions of python 2.7+
However if the tests are run by pytest
the above error is not thrown. This was likely fixed in the latest pytest resulting in the above error.
It's a simple fix with 2 changes, from unittest.mock import patch
for Python 3.
This could be a newcomer issue. If not, I want to be assigned.
@frextrite , if you want to explore your pytest
theory, the versions used can be found in the logs at https://build.opensuse.org/package/show/home:jayvdb:coala/python-coala-bears , and you can 'branch' that package and edit the .spec to remove the rm tests/coalaBearTest.py
which I did as a quick hack.
@areebbeigh, that sounds like it might be the fix, but how do we prevent it from occurring again? If you can think of a solution to that, this becomes a viable non-newcomer issue.
@jayvdb We should go with the fix suggested by @areebbeigh, changing import unittest
to from unittest.mock import patch
.
but how do we prevent it from occurring again?
This would a hardbound fix and it would never occur again. The old method being used in coalaBearTest.py
was bound to fail in every version of python but pytest with its magic made it work somehow. The new import method would never fail as it is the recommended(only correct) way of using the patch decorator.
was bound to fail in every version of python but pytest with its magic made it work somehow.
But we use pytest, and it does this magic, and it doesnt work with the pytest version that distros currently provide even in their rolling release. That means developers are likely going to try using the pytest from their OS and it will fail.
Someone can re-introduce this bad import and our CI wont fail. Expecting people to follow guidelines doesnt work -- if it did, this import wouldnt exist.
Stumbled upon the following here:
Improved reporting of mock call assertion errors This plugin monkeypatches the mock library to improve pytest output for failures of mock call assertions like Mock.assert_called_with() by hiding internal traceback entries from the mock module.
The following config in pytest.ini disables this plugin and the AttributeError is detected for the bad import:
[pytest]
mock_traceback_monkeypatch = false
PyTest version: v3.6.4
__________________________________________ ERROR collecting tests/coalaBearTest.py ___________________________________________
tests/coalaBearTest.py:10: in <module>
class coalaBearTest(unittest.TestCase):
tests/coalaBearTest.py:29: in coalaBearTest
@unittest.mock.patch('sys.version_info', tuple((2, 7, 11)))
E AttributeError: module 'unittest' has no attribute 'mock'
Lovely @areebbeigh . Please check that setting on coala repo also, and then submit a MR to https://github.com/coala/mobans with last line Related to https://github.com/coala/coala-bears/issues/2862
, adding it to the template for all coala python repos
I think this might require a little more investigation. I'm running into strange behaviour now:
coalaDeleteOrigTest.py in the coala repo uses mock.patch in the same broken way but still passes when I run it with nosetests
.
The line - check the comment belowfrom coalib import coala_delete_orig
for some reason passes the test. Tried pasting it into coalaBearTest.py
and it causes the test to pass.
@jayvdb
After looking into the requirements and test behaviours I found (oddly):
In the coala repo:
coala-utils==0.7.0
causes the tests to pass with bad imports.
coala-utils==0.6.7
doesn't seem to have this issue. The tests behave as expected (i.e throw AttributeError for bad imports) in this case.
In general:
This fix is still applicable but not for coala-utils==0.7.0
. Further investigation needed.
@areebbeigh , you may be seeing an example of an import changing state.
i.e. if from unittest.mock import patch
is done first, from unittest import mock; mock.patch
may become valid afterwards but not before. Just an idea. I've seen it happen before.
It's testfixtures
causing the problems. Turns out, coala-utils starting from 0.7.0 is using testfixtures therefore any import from coala-utils using testfixtures causes this behaviour. You've already created an issue: Remove dependency on textfixtures (Related: https://github.com/coala/coala/issues/5335)
TL;DR: For a fix, testfixtures should only be a test-requirement and never otherwise.
So we only have a fix for tests not depending on coala-utils==0.7.0
(directly or indirectly) for now. I assume this issue is blocked until coala-utils 0.8.0?
Currently the master branch for coala-utils isn't using this library out of tests (removed here).
To confirm:
This test will fail (AttributeError) if import testfixture
is commented out and pass otherwise.
import unittest
import sys
import os
import testfixtures
class Test(unittest.TestCase):
@unittest.mock.patch('sys.version_info', (2,7,0))
def test_version(self):
os.system(f"echo '{sys.version_info}'")
assert(sys.version_info < (3,0,0))
I assume this issue is blocked until coala-utils 0.8.0?
Yup. Great analysis. I'll leave this assigned to you, and give you tasks related to getting coala-utils 0.8.0 released. It depends on PyPrint being released, and that is almost done, but there are a few more things which should be done.
Needs a status/blocked
@jayvdb
Please take a look upstream to see if testfixtures
is doing anything funky which might be causing this.
We are using an old pinned version of testfixtures
, so the problem might be fixed in more recent versions.
Relying on an import sequence isnt 'safe'.
This could be an extension of https://gitlab.com/coala/bears/coala-pyflakes/issues/10 , as it is easy to determine that unittest.mock
is a sub-module by attempting to import it, and thus should not be accessed as a property of unittest
.
But the problematic import needs to be fixed asap, so we cant block this. If we cant prevent it from happening quickly, we need to raise a separate issue about it, or include it as part of another issue (e.g. the one above) so it isnt forgotten.
Please take a look upstream to see if
testfixtures
is doing anything funky which might be causing this.We are using an old pinned version of
testfixtures
, so the problem might be fixed in more recent versions.
The latest version for testfixtures (6.5.0) is the same.
Relying on an import sequence isnt 'safe'.
Then I'll create a PR changing all the imports to the form below whereever necessary?
import unittest
import unittest.mock
But the problematic import needs to be fixed asap, so we cant block this. If we cant prevent it from happening quickly, we need to raise a separate issue about it or include it as part of another issue (e.g. the one above) so it isn't forgotten.
It can probably be fixed in all cases. I'll look through it (including the core repo).
Should I open a separate issue on the coala repo for these imports or I can just refer this one in a PR?
About preventing from the errored importing from passing the tests in the future I'd like to recap that it'll be taken care of as follows:
Should I open a separate issue on the coala repo for these imports or I can just refer this one in a PR?
Yea, we only need one issue - you can do all the PRs.
An MR to the mobans repo has been made disabling monkeypatch for pytest
Im not sure about this yet - it sounds like it is useful ...
testfixture won't be a (non test) dependency anymore in coala-utils 0.8.0
And here is the real problem ... even if it is removed from coala-utils source, we still might do import testfixtures
in the tests, and the problem can re-occur.
The problem in testfixtures
needs to be found and fixed.
And a linter is still needed to catch this type of problem.
And if those two are done, we dont need to disable the monkey patch magic in pytest.
Not an issue with testfixtures. Here's what's happening: From here and https://github.com/Simplistix/testfixtures/issues/108
when a submodule is loaded anywhere it is automatically added to the global namespace of the package
$ python3
>>> import logging
>>> logging.config
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'config'
$ echo "import logging.config" > weirdimport.py
$ python3
>>> import weirdimport
>>> import logging
>>> logging.config
<module 'logging.config' from '/usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/logging/config.py'>
So it's just the way imports are dealt with in Python.
Yup, which is why we need a linter to protect against this part of the Python runtime.
Seen on Python 3.7