Skipping import modules in pytest from the command line - python

The documentation for pytest suggests you can skip certain imports:
https://docs.pytest.org/en/latest/skipping.html#skipping-on-a-missing-import-dependency
We are trying to run pylint under pytest and in some cases importing tensorflow causes issues because of system dependencies. The documentation shows a way of skipping the import in code, is it possible to skip imports like this from the command line of pytest?

There is no such feature in pytest, so you should do this directly in code (usually in a conftest.py).
A hacky workaround to do the same directly at the command line woud be:
python -c "import pytest; pytest.importorskip('tensorflow'); pytest.main()"
Better would be to use one of the existing hooks to add your own command-line option to pytest, so it can be specified clearly like --no-tensorflow or whatever.

Related

How pylint-pytest throws F6401 Can-enumerate-pytest-fixtures

Can you explain the prompt 'F6401' when I run pylint pylint-pytest plugin cannot enumerate and collect pytest fixtures. Please run `pytest --fixtures --collect-only path/to/current/module.py` and resolve any potential syntax error or package dependency issues (Can-enumerate-pytest-fixtures) is the reason?
I would like to know how it works, or why it appears, and sometimes has different outputs. The same code, sometimes two, sometimes more. I was depressed.
I did run pytest --fixtures --collect-only without any unusual hints and my tests were normal.
Description:
After I fine-tune my existing code, including running pylint, pytest, and isort, everything works. I added a new package executor with three modules, one is the abstract module of base.py, two are corresponding to different implementation modules(local.py, docker.py).
Then I run isort, and pylint works fine
Then I import the base class and two implementation classes in the module's __init__.py file, and add a factory method.
When I run pylint again, the input tells me that some of the test modules have problems with F6401.
Again, I want to emphasize that everything was fine until I added this module. But now I just added the source code of this module, this exception will appear.
What makes it even more confusing to me is that the module I'm prompted doesn't include any fixtures. I ran pylint again and found that F6401 has more test modules (several times more than last time).
I've been using PyLint for a new project to check for a mode-by-module migration, and when I migrate to this module, I can't continue.
OS env
python 3.7
os: Deepin(base Debian)
IDE: Pycharm
Package versions
pylint 3.0.0a3
pylint-pytest 1.1.2
pyparsing 2.4.7
pytest 6.2.3
pytest-asyncio 0.14.0
pytest-cov 2.11.1
pytest-mock 3.5.1
ISSUE about this question.
After debugging the source code, I found out that the cause of my problems was an error in pylint-pytest when running pytest to collect fixtures from source code, and then pylint-pytest passed the error to PyLint.
My source code had a type annotation error that caused pytest to look for a fixture from that module that was wrong, and the error was passed to pylint. But why there is a different output is not clear to me.
From debugging the source code, we know that pylint-pytest registers itself with pylint, and when pylint checks all files, it passes the files to pylint-pytest's FixtureChecker.
https://github.com/reverbc/pylint-pytest/blob/62676386f80989cc0373d77bc5dc74acc635fd7a/pylint_pytest/checkers/fixture.py#L92-L142
The visit_module method in the FixtureChecker passes the file to pytest, running pytest <module_file> --fixtures --collect-only, At the same time load the FixtureCollector plug-in into pytest.
https://github.com/reverbc/pylint-pytest/blob/62676386f80989cc0373d77bc5dc74acc635fd7a/pylint_pytest/checkers/fixture.py#L125-L131
In pytest_collectreport , if an error is reported by pytest, it is logged and the error information is passed to pytest.
https://github.com/reverbc/pylint-pytest/blob/62676386f80989cc0373d77bc5dc74acc635fd7a/pylint_pytest/checkers/fixture.py#L24-L34
I don't think this logic makes sense. Pytest should only collect fixtures from the test modules, and instead of collecting fixtures from all modules, Pylint-Pytest should filter out the source code when PyLint checks.
At this point, my doubts have disappeared. Thanks.

SciPy/pytest: Skip specific test

I test my SciPy installation using
python -c "import scipy; scipy.test('full', verbose=2)"
A single test (test_face) fails, while all other either passes or xfails. This one test fails because the dependency bz2 is lacking, which is fine. How can I specify that I want to skip this test entirely, while still running all other tests?
I'm using SciPy 1.2.0 with pytest 4.0.2.
I found a working solution using the extra_argv argument which passes the arguments on to pytest. From the pytest docs, -k "not test_face" may be used to skip exactly this test. In total then,
python -c "import scipy; scipy.test('full', verbose=2, extra_argv=['-k not test_face'])"
achieves what I wanted.

Code coverage does not cover dynamically imported packages/modules

I am using the following command to run tests:
nosetests --with-coverage --cover-html --cover-package mypackage
I would like the coverage report to be updated, even if a developer adds new, untested, code to the package.
For example, imagine a developer adds a new module to the package but forgets to write tests for it. Since the tests may not import the new module, the code coverage may not reflect the uncovered code. Obviously this is something which could be prevented at the code review stage but it would be great to catch it even earlier.
My solution was to write a simple test which dynamically imports all modules under the top-level package. I used the following code snippet to do this:
import os
import pkgutil
for loader, name, is_pkg in pkgutil.walk_packages([pkg_dirname]):
mod = loader.find_module(name).load_module(name)
Dynamically importing sub-packages and sub-modules like this does not get picked up by the code coverage plugin in nose.
Can anyone suggest a better way to achieve this type of thing?
The problem seems to be the method for dynamically importing all packages/modules under the top-level package.
Using the method defined here seems to work. The key difference being the use of importlib instead of pkgutil. However, importlib was introduced in python 2.7 and 3.1 so this solution is not appropriate for older versions of python.
I have updated the original code snippet to use __import__ instead of the ImpLoader.load_module method. This also seems to do the trick.
import os
import pkgutil
for loader, name, is_pkg in pkgutil.walk_packages([pkg_dirname]):
mod = loader.find_module(name)
__import__(mod.fullname)

How to prevent nose from importing __init__.py files?

Can the nose testing framework be instructed to only run tests in test_*.py files?
In fact, doing nosetests A with the following directory structure:
A/
test_A.py
B/
__init__.py
imports B, which I want to avoid.
The reason for this is that the B module starts with import numpy because it is only meant to be used when the user has the optional NumPy module installed. However, users who did not install NumPy do not want nosetests to process B/__init__.py, because it necessarily fails on import numpy even though NumPy is optional. How can this be achieved?
Simply wrap the import with a try:..except ImportError:... block. In that case you could even set a variable letting you know if numpy is available or not.
Sure, just use the --match and the --exclude command line options to limit what nose will discover as a test program.
Nose can accept/reject tests at the directory, file, module, class and method levels. You need to reject the B directory. There is no way to ignore the __init__.py file; Python never sees it. It shows up when B is imported, so you need to ignore B.
Try:
nosetests --exclude=B
I think the nose-exclude plugin may help. If you install this plugin and then run:
nosetests --exclude-dir B
It may work for you. I have the same problem and have achieved some passable results. Now my next issue is that the exclude-dir doesn't seem to be a usable option when creating a nosetests config file.

setup.py adding options (aka setup.py --enable-feature )

I'm looking for a way to include some feature in a python (extension) module in installation phase.
In a practical manner:
I have a python library that has 2 implementations of the same function, one internal (slow) and one that depends from an external library (fast, in C).
I want that this library is optional and can be activated at compile/install time using a flag like:
python setup.py install # (it doesn't include the fast library)
python setup.py --enable-fast install
I have to use Distutils, however all solution are well accepted!
The docs for distutils include a section on extending the standard functionality. The relevant suggestion seems to be to subclass the relevant classes from the distutils.command.* modules (such as build_py or install) and tell setup to use your new versions (through the cmdclass argument, which is a dictionary mapping commands to classes which are to be used to execute them). See the source of any of the command classes (e.g. the install command) to get a good idea of what one has to do to add a new option.
An example of exactly what you want is the sqlalchemy's cextensions, which are there specifically for the same purpose - faster C implementation. In order to see how SA implemented it you need to look at 2 files:
1) setup.py. As you can see from the extract below, they handle the cases with setuptools and distutils:
try:
from setuptools import setup, Extension, Feature
except ImportError:
from distutils.core import setup, Extension
Feature = None
Later there is a check if Feature: and the extension is configured properly for each case using variable extra, which is later added to the setup() function.
2) base.py: here look at how BaseRowProxy is defined:
try:
from sqlalchemy.cresultproxy import BaseRowProxy
except ImportError:
class BaseRowProxy(object):
#....
So basically once C extensions are installed (using --with-cextensions flag during setup), the C implementation will be used. Otherwise, pure Python implementation of the class/function is used.

Categories