-
-
Notifications
You must be signed in to change notification settings - Fork 411
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
Skip tests for optional/extra dependencies when not installed #1113
Conversation
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.
Very general and minor comments.
To get pylint off you back you can put the emcee = pytest.importorskip("emcee")
like lines below the imports, or set the disable wrong import error at the beginning of the file to avoid the check throughout all the file.
I'd also change the message in the skipifs to "xx not installed" or "test requires xx which is not installed"
Regarding numba, maybe we could move all numba tests to a specific file? Numba is an optional requirement for users (like bokeh) whereas the dependencies in requirements dev are not.
Hi @OriolAbril thanks! I'll fix the I'll update the skipif messages to: "test requires xx which is not installed" I'll look into moving all the
The easiest/laziest solution might be to create new files with
This would preserve the clarity of the original goal that |
2f43704
to
ea585c9
Compare
Hi @OriolAbril I ended up moving the |
import stan as pystan # pylint: disable=import-error | ||
return int(pystan.__version__[0]) | ||
try: | ||
import stan as pystan # pylint: disable=import-error |
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.
Curious, why is this extra block of logic needed?
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.
@canyon289 In the event that neither pystan
or stan
are installed, there would be no imported pystan
, which would then have no version attribute. I believe that this function is called via test_data_pystan.py
, and will throw an error just while collecting the tests. Even though this file would eventually be skipped because neither libraries are installed via:
pytestmark = pytest.mark.skipif(
~pystan_installed, reason="test requires stan/pystan which is not installed"
)
This was my way to handle potentially two exceptions, and also indicate that there is no form of stan
installed by returning None
. (I have neither stan
/pystan
package, so this was a solution that I could get to work for me)
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.
Yeah, this sounds fine, maybe this could be import stan
--> stan.__version__
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.
Hi @ahartikainen is that just regarding line 580? Still try to import pystan
, and if not present try to import stan
, but no need to import stan as pystan
?
This pr is great. It does mean that the maintainers need to be extra vigilant with CI and ensure that no tests are skipped before merging. Do you know if theres a way with pytest to fail ir any tests are skipped? |
Valid point @canyon289 ! There may be an option that is builtin to
But I must imagine that there might be something completely integrated into |
Could we have env variable which is also tested with the import func? |
Curious what this would do |
I have to point out that some tests are skipped in CI, some features require the latest version of a given library which means that test can be executed in "external_latest" but not in "external_especial" |
I'm guessing @ahartikainen is proposing to create an env variable on the CI machine like if os.environ.get('ARVIZ_CI_MACHINE')=='True':
ARVIZ_CI_MACHINE = True
else:
ARVIZ_CI_MACHINE = False
try:
import pystan
pystan_installed = True
except ImportError:
pystan_installed = False
pytestmark = pytest.mark.skipif(
~pystan_installed & ~ARVIZ_CI_MACHINE, reason="test requires stan/pystan which is not installed"
) |
@OriolAbril so it would be a good idea to separate acceptable and unacceptable CI skips? |
Maybe we could monkeypatch pytest.importskip function? |
I have seen this example in pytest docs about importorskip usage: http://doc.pytest.org/en/latest/example/parametrize.html#indirect-parametrization-of-optional-implementations-imports, maybe we could base the monkeypatch on something similar? If CI import and return module, if not in CI use importorskip? |
I've been looking at monkeypatching @pytest.fixture(autouse=True)
def no_importorskip(monkeypatch):
"""Remove pytest.importorskip for all tests."""
monkeypatch.delattr("pytest.importorskip") I think if we want to go down this route, we can implement our own |
yeah, that sounds like the correct option |
@ahartikainen let me know of this implementation. I also added tests for the custom |
0efed7a
to
0a55e59
Compare
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.
Looks good. I added some minor comments / questions.
LGTM
import stan as pystan # pylint: disable=import-error | ||
return int(pystan.__version__[0]) | ||
try: | ||
import stan as pystan # pylint: disable=import-error |
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.
Yeah, this sounds fine, maybe this could be import stan
--> stan.__version__
# of existing directories with the same name we're trying to | ||
# import but without a __init__.py file | ||
warnings.simplefilter("ignore") | ||
__import__(modname) |
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.
is there any difference between these
__import__(modname)
mod = sys.modules[modname]
vs
mod = importlib.import_module(modname)
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.
I think they should be mostly similar for our uses. I used the former as I copied the existing code in pytest.importorksip
.
But I can use importlib.import_module
. It might clear up the failures in CI to import jax.random.PRNGKey
and pyro.infer.Predictive
.
I can't seem to easily install jaxlib
on Windows, so I can't test this out locally.
In addition to the points raised by @ahartikainen , I'm looking to see why these CI imports failed:
|
@ahartikainen I don't think |
tbh, it is not a test, but a way to precompile models for CI, so at some point, we can start gathering performance data for our tests and they should be reproducible, in most cases. I cheated with So users should not run it (without manually calling it) |
edit.
https://stackoverflow.com/questions/41678073/import-class-from-module-dynamically |
okay got it @ahartikainen
I'll give that shot! |
2bb6522
to
b8ad150
Compare
Codecov Report
@@ Coverage Diff @@
## master #1113 +/- ##
==========================================
+ Coverage 92.61% 92.68% +0.06%
==========================================
Files 93 93
Lines 8991 9073 +82
==========================================
+ Hits 8327 8409 +82
Misses 664 664
Continue to review full report at Codecov.
|
Is this now skipping pystan tests? |
Seems that way. I was going to investigate why. To sort this out, I setup a virtual machine to mimic the CI, but pystan tests ran locally. |
Is it trying to import once and then just fails? |
importlib.util.find_spec("stan") is not None | ||
) | ||
pytestmark = pytest.mark.skipif( | ||
not pystan_installed & (not running_on_ci()), |
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.
not pystan_installed & (not running_on_ci())
not True & (not True) == True
(not True) & (not True) == False
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.
Or you can use
not ( pystan_installed | running_on_ci() )
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.
I think that logic is cleaner. But shouldn't the current logic work because it equates to False, so it shouldn't get skipped
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.
It is currently true (missing parenthesis)
not True & (not True) == True
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.
Ah yes okay. I updated that, will see if it does the trick. And I see that there are some built-in emcee skips looking for emcee3, so that should still be okay
Thanks @ahartikainen for getting this through! |
Looked it over. It looks like it works, there some sections I can't read but I don't want to hold this up :) |
Also this is great work. Looks well implemented. thank you so much for persevering and getting it through |
@canyon289 there are sections you physically can't access in order to inspect, or some sections that don't make sense? If there's something I could clarify or improve to avoid future headache, I'd be happy to update it now that it's working. Happy to contribute 🙂 |
@hectormz It's likely less about your code and more about me not understanding the depth of the pytest internals. I didn't mean that as a criticism of your work! |
No offense taken! |
Description
When running all tests locally
ImportError
s are thrown if optional/extra dependencies are not installed. By skipping these tests when not installed, users can still run the entire test suite if desired. The entire test suite should still be run via CI.This behavior is largely achieved by changing
to
which will try to import module
xx
, and will skip all tests in file if the module is not installed.Individual tests may also be skipped using the decorator:
@pytest.mark.skipif(importlib.util.find_spec("xx") is None, reason="xx tests only required for CI")
The last remaining issue is
numba
, which is used in 20+ tests, used in a variety of files. One option is to makenumba
an entry inrequirements-dev.txt
instead ofrequirements-optional.txt
, or add the skip decorators to each of the 20+ tests.This PR addresses Issue #1112 .
Checklist