-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Fix the stubgen test flake. #4811
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
Conversation
The core problem is that `importlib.invalidate_caches()` is full of
lies and does not actually invalidate all of the relevant
caches. Python maintains sys.path_importer_cache, which maps from
directory names on the sys.path to "importer" objects for that path
entry. `importlib.invalidate_caches()` invalidates the caches inside
each of the per-directory importer objects, but there is an additional
negative cache that is *not* cleared: sys.path entries whose directory
doesn't exist have their importer set to None, which prevents that
path entry from ever being searched unless it is manually cleared.
This failure comes up rarely, because it only occurs if the following
events occur in sequence:
1. 'stubgen-test-path' is added to sys.path, but no new import is
done while there is a 'stubgen-test-path' subdirectory in the working
directory. This is done by all the stubgen tests that don't end with
`_import`.
2. Some non-stubgen test does an import of a previously unimported
module. This will cause
sys.path_importer_cache['stubgen-test-path'] to be set to None
when the directory doesn't exist during the module search.
This can happen the *first* time that a `_python2` test is run
since `parse.parse()` dynamically imports `mypy.fastparse2`
when asked to parse python 2.
3. A stubgen test tries to use importlib to import a module in
'stubgen-test-path'. All of the `_import` tests do this.
We fix this by clearing out the relevant entry from
`sys.path_importer_cache` ourselves.
|
Testing this was somewhat annoying, since pytest doesn't make it easy to control ordering of tests. What I ended up doing was creating a and then running |
JukkaL
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, impressive analysis! It will be great to finally get rid of this test flake. Thanks for fixing this!
I guess another way to work around the issue would be to only add randomly generated path names to sys.path. Maybe mention this in a comment somewhere in case we'd need to change the approach for some reason?
Also left one note about a comment.
mypy/test/teststubgen.py
Outdated
| pass | ||
| def reset_importlib_cache(entry: str) -> None: | ||
| # importlib.invalidate_caches() is insufficient, since it doesn't | ||
| # clear cache entries indicate that a directory on the path does |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is something missing in this sentence. Maybe this should be "... clear cache entries that indicate ..."
|
Great analysis. I filed a CPython bug at https://bugs.python.org/issue33169.
|
While trying to test mypy_mypyc wheels in travis on OS X, I pretty consistently got failures trying to import a plugin module. I had trouble reproducing it anywhere else, but the issue turned out to be a cousin of the dreaded stubgen flake (#4811), where caching in importlib causes a file to be missed even though it is present now. Here we solve it by using absolute paths in the `load_plugins` manipulations of sys.path.
…5937) While trying to test mypy_mypyc wheels in travis on OS X, I pretty consistently got failures trying to import a plugin module. I had trouble reproducing it anywhere else, but the issue turned out to be a cousin of the dreaded stubgen flake (#4811), where caching in importlib causes a file to be missed even though it is present now. Here we solve it by using absolute paths in the `load_plugins` manipulations of sys.path.
The core problem is that
importlib.invalidate_caches()is full oflies and does not actually invalidate all of the relevant
caches. Python maintains sys.path_importer_cache, which maps from
directory names on the sys.path to "importer" objects for that path
entry.
importlib.invalidate_caches()invalidates the caches insideeach of the per-directory importer objects, but there is an additional
negative cache that is not cleared: sys.path entries whose directory
doesn't exist have their importer set to None, which prevents that
path entry from ever being searched unless it is manually cleared.
This failure comes up rarely, because it only occurs if the following
events occur in sequence:
done while there is a 'stubgen-test-path' subdirectory in the working
directory. This is done by all the stubgen tests that don't end with
_import.module. This will cause
sys.path_importer_cache['stubgen-test-path']to be set to Nonewhen the directory doesn't exist during the module search.
This can happen the first time that a
_python2test is runsince
parse.parse()dynamically importsmypy.fastparse2when asked to parse python 2.
'stubgen-test-path'. All of the
_importtests do this.We fix this by clearing out the relevant entry from
sys.path_importer_cacheourselves.Fixes #4635