6

I'm using tox and coverage.py to run tests of my Python project in my continuous build server. I also have a package pkg_x from a vendor (not available on PyPI) that I've installed using python3.5 setup.py install, which puts it in /usr/lib/python3.5/site-packages. Now I need to make that package available to the testing code.

My current tox.ini looks like this:

[tox]
envlist = py35

[testenv]
deps = nose
       coverage
commands = coverage run -m nose []
sitepackages = True

and I run the tests like so:

python3.5 -m tox -- --verbose --with-doctest

That fails spectacularly - none of the dependency packages listed in my local setup.py (e.g. public stuff like more_itertools) can be found, even though it does create directories like .tox/py35/lib/python3.5/site-packages/more_itertools that seem to contain the relevant packages. If I fire up .tox/py35/bin/python3.5, sys.path looks like this:

>>> [re.compile('.*\\.tox').sub('.tox', x) for x in sys.path]
['',
 '.tox/py35/lib64/python35.zip',
 '.tox/py35/lib64/python3.5', 
 '.tox/py35/lib64/python3.5/plat-linux',
 '.tox/py35/lib64/python3.5/lib-dynload',
 '/usr/lib64/python3.5',
 '/usr/lib/python3.5',
 '.tox/py35/lib/python3.5/site-packages']

If I remove the sitepackages = True line from my tox.ini, then I do get farther, in that packages like more_itertools and the rest of the stuff in my setup.py dependencies can now be found, but the vendor package pkg_x I mentioned above still can't be found. And sys.path looks like this:

>>> [re.compile('.*\\.tox').sub('.tox', x) for x in sys.path]
['',
 '.tox/py35/lib64/python35.zip',
 '.tox/py35/lib64/python3.5', 
 '.tox/py35/lib64/python3.5/plat-linux',
 '.tox/py35/lib64/python3.5/lib-dynload',
 '/usr/lib64/python3.5',
 '/usr/lib/python3.5',
 '.tox/py35/lib/python3.5/site-packages',
 '/usr/lib64/python3.5/site-packages',
 '/usr/lib/python3.5/site-packages']

In neither case does .tox/py35/ seem to contain the vendor package pkg_x anywhere. And although the directory /usr/lib/python3.5/site-packages is listed when I fire up .tox/py35/bin/python3.5 manually, pkg_x isn't actually found when running the tests.

It also looks like sitepackages = True has the opposite effect from what it's documented to do at http://tox.readthedocs.io/en/latest/config.html#confval-sitepackages=True|False , right?

Advice very appreciated!

Ken Williams
  • 22,756
  • 10
  • 85
  • 147
  • I put a comment on a Tox issue that looks like the same thing I'm seeing: https://github.com/tox-dev/tox/issues/461#issuecomment-303855697 – Ken Williams May 25 '17 at 16:45
  • `tox` creates it's own virtualenv's. How familiar are you with virtualenv? Activate the venv in the `.tox` directory and debug from there. – John Mee May 26 '17 at 01:34

2 Answers2

5

Tox creates a virtualenv, then runs it's tests from inside that environment.

The --sitepackages argument is a switch determining whether the virtualenv gets access to globally installed packages.

The 'normal' way to run tox would be to simply say tox; installing it via pip or via an OS package should put it into your path. ie:

$ tox

Which is the same as saying:

$ tox -c tox.ini

Where you invoke tox directly, python -m tox, it may do something, but this is a red flag to me. This command seems unlikely to activate the relevant virtual environment, which would explain your package availability woes. It fits because when you omit sitepackages, it actually adds the global packages because it thinks it is inside a virtualenv and so adds, what it thinks are the local sitepackages, although they're actually global. The reverse happens when you make it true because when it goes looking for global packages it can't find them. Whatever, because you're not invoking tox as expected it's as confused as we are.

So just use the tox command provided.

But wait there's more: You say you have a package which is required, but not available on pypi. So how is tox going to install it? The tox docs suggest a couple of ways (use a requirements.txt), but the most direct to describe here is to activate the env and install it manually.

Also good if you need to debug further: step into the .tox directory and activate the venv manually. eg:

$ source .tox/testenv/bin/activate

(where testenv is the name you used between brackets in the tox.ini).

Now install the package however you did it before eg: pip install pkg_x

Deactivate the venv when you're done with it this way:

$ deactivate

Try tox now?

If we're on the right track learn more about virtualenv here

John Mee
  • 50,179
  • 34
  • 152
  • 186
  • Thanks - I'm sure you're absolutely right that my understanding needs work. However, the suggested solution of pre-installing into the virtual environment won't work, because this is in a continuous build server, where the working environment is rebuilt with every run. I'd have to install it every time a test is run, and the installation needs human interaction. – Ken Williams May 26 '17 at 03:37
  • The reason I'm using `python3 -m tox` instead of simply `tox` is so that I can control which version of python is used to invoke Tox, rather than depending on the shebang line in the `tox` script. I thought this was a supported use case? – Ken Williams May 26 '17 at 03:44
  • 1
    For the dependancy use tox's `DEPS=`. For the versions, that's the key sales pitch of tox... it tests multiple versions of python simultaneously. You can configure each environment in the tox then it will build and test on each. [Check the manual ;-)](http://tox.readthedocs.io/en/latest/example/basic.html) – John Mee May 26 '17 at 03:49
2

It looks like the solution to my problem is to change the commands line in my tox.ini from this:

commands = coverage run -m nose []

to this:

commands = python -m coverage run -m nose []

The effect is that python is now a virtualenv-mapped command, which loads the correct Python interpreter, loads the coverage module, and runs it. Without python -m, it just finds whatever coverage executable is in my PATH and runs it, with unpredictable results. whitelist_externals could be used, but it would only band-aid the problem.

Then if I also add the sitepackages = True line, it successfully sees the pkg_x I installed from the vendor, and my tests succeed.

Ken Williams
  • 22,756
  • 10
  • 85
  • 147