Skip to content
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

nox session doesn't find pytest installed by poetry #101

Open
Dominik1123 opened this issue Jun 8, 2020 · 21 comments
Open

nox session doesn't find pytest installed by poetry #101

Dominik1123 opened this issue Jun 8, 2020 · 21 comments

Comments

@Dominik1123
Copy link

I'm following along the Hypermodern Python series and it seems in Chapter 2, Test automation with Nox there is a conflict between nox and poetry. Specifically when I use the example noxfile.py:

import nox

@nox.session(python=["3.8", "3.7"])
def tests(session):
    session.run("poetry", "install", external=True)
    session.run("pytest", "--cov")

I get the following error:

nox > Running session tests-3.8
nox > Creating virtual environment (virtualenv) using python3.8 in .nox/tests-3-8
nox > poetry install
Installing dependencies from lock file

No dependencies to install or update

  - Installing testpkg (0.1.0)
nox > Program pytest not found.
nox > Session tests-3.8 failed.

(and the same for tests-3.7)

It seems that installing pytest via poetry as a dev dependency doesn't put it on the path. In the previous section of Chapter 2, pytest was invoked through poetry and this does work in the nox session as well:

session.run('poetry', 'run', 'pytest', '--cov')

Is this an issue with my setup or is it expected that pytest can't be run as a command when installed by poetry?

@cjolowicz
Copy link
Owner

Interesting. Which versions of Poetry and Nox do you use?

@Dominik1123
Copy link
Author

Dominik1123 commented Jun 9, 2020

I'm using these versions:

$ nox --version
2020.5.24
$ poetry --version
Poetry version 1.0.8
$ poetry run pytest --version
This is pytest version 5.4.3

@Dominik1123
Copy link
Author

I get a somewhat related error using the noxfile.py from Chapter 3. Now the situation is inverted: dev dependencies are install by Nox via session.install (i.e. pip) and the to-be-tested package is installed via Poetry: session.run('poetry', 'install', '--no-dev', external=True). Now invoking pytest directly works (i.e. session.run('pytest', *args)) however it doesn't find the to-be-tested package:

nox > Running session tests
nox > Creating virtual environment (virtualenv) using python3.8 in .nox/tests
nox > poetry export --dev --format=requirements.txt --output=/tmp/tmpoihujsek
nox > pip install --constraint=/tmp/tmpoihujsek pytest pytest-cov pytest-mock coverage[toml]
nox > poetry install --no-dev
Installing dependencies from lock file

No dependencies to install or update

  - Installing testpkg (0.1.0)
nox > pytest --cov
==================================== test session starts =====================================
platform linux -- Python 3.8.3, pytest-5.4.3, py-1.8.1, pluggy-0.13.1
rootdir: /path/to/testpkg
plugins: cov-2.9.0, mock-3.1.1
collected 0 items / 3 errors                                                                 
Coverage.py warning: Module testpkg was never imported. (module-not-imported)
Coverage.py warning: No data was collected. (no-data-collected)
WARNING: Failed to generate report: No data to report.

/path/to/testpkg/.nox/tests/lib/python3.8/site-packages/pytest_cov/plugin.py:264: PytestWarning: Failed to generate report: No data to report.

  self.cov_controller.finish()

=========================================== ERRORS ===========================================
____________________________ ERROR collecting tests/test_base.py _____________________________
ImportError while importing test module '/path/to/testpkg/tests/test_base.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
tests/test_base.py:6: in <module>
    import testpkg.base
E   ModuleNotFoundError: No module named 'testpkg'

I suppose this is because both Nox and Poetry maintain their own virtual environments and pytest has been installed into the Nox environment while testpkg has been installed into the Poetry environment? How are they supposed to know each other?

@cjolowicz
Copy link
Owner

Thank you for the detailed information!

Can you also paste the output of poetry config --list?

So far, I have not been able to reproduce this.

@cjolowicz
Copy link
Owner

cjolowicz commented Jun 10, 2020

I have been able to reproduce this. Looks like Poetry silently falls back to creating its own virtual environment if it cannot use the virtual environment it's running in. Can you try removing both the Nox and Poetry environments for your project?

cd path/to/hypermodern-python
rm -rf .nox
poetry env remove 3.8

Specifically, I did the following to reproduce the issue:

git clone https://github.com/cjolowicz/hypermodern-python.git
cd hypermodern-python/
git switch chapter02
nox -s tests -p 3.8
docker run --rm -ti -v $(pwd):/src -w /src python:3.8.3 bash
# in the container:
pip install nox==2020.5.24
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
source $HOME/.poetry/env
nox -s tests -p 3.8 -r

The output shows that Poetry creates its own virtual environment instead of installing into the existing Nox environment:

nox > Running session tests-3.8
nox > Re-using existing virtual environment at .nox/tests-3-8.
nox > poetry install --no-dev
Creating virtualenv hypermodern-python-VsnhxLU2-py3.8 in /root/.cache/pypoetry/virtualenvs

In this case, the environment is unusable for Poetry because it was created outside of the container. The shebangs in the entrypoint scripts point to a Python interpreter located outside of the container.

nox > pytest --cov -m not e2e
nox > Session tests-3.8 raised exception FileNotFoundError(2, 'No such file or directory')
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/nox/sessions.py", line 462, in execute
    self.func(session)
  File "/usr/local/lib/python3.8/site-packages/nox/_decorators.py", line 53, in __call__
    return self.func(*args, **kwargs)
  File "/src/noxfile.py", line 8, in tests
    session.run("pytest", *args)
  File "/usr/local/lib/python3.8/site-packages/nox/sessions.py", line 226, in run
    return self._run(*args, env=env, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/nox/sessions.py", line 254, in _run
    return nox.command.run(args, env=env, path=self.bin, **kwargs)
  File "/usr/local/lib/python3.8/site-packages/nox/command.py", line 109, in run
    return_code, output = popen(
  File "/usr/local/lib/python3.8/site-packages/nox/popen.py", line 35, in popen
    proc = subprocess.Popen(args, env=env, stdout=stdout, stderr=stderr)
  File "/usr/local/lib/python3.8/subprocess.py", line 854, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/usr/local/lib/python3.8/subprocess.py", line 1702, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: '/src/.nox/tests-3-8/bin/pytest'
nox > Session tests-3.8 failed.
# head /src/.nox/tests-3-8/bin/pytest
#!/private/tmp/hypermodern-python/.nox/tests-3-8/bin/python
# -*- coding: utf-8 -*-
import re
import sys
from pytest import main
if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(main())

@Dominik1123
Copy link
Author

I just checked and the tests run without problem on the CI server. Running locally on my machine however gives the above error. Removing the .nox and pytest environments unfortunately didn't help. It still complains that Program pytest not found. I can confirm that poetry install creates its own venv besides the already existing nox-venv. It's not completely clear to me what makes the difference between my local setup and the CI server (here the log shows that poetry doesn't create its own venv).

@cjolowicz
Copy link
Owner

cjolowicz commented Jun 15, 2020

Thanks, some more questions then:

  • Which OS do you use? (Linux, judging from your pasted output)
  • Does the file .nox/tests-3-8/bin/pytest exist?
  • If yes, what's the first line in that file? Is the path on that line located in the Nox environment?
  • What is the output of poetry config --list?

@Dominik1123
Copy link
Author

I'm on Ubuntu 16.04. The file .nox/tests-3-8/bin/pytest doesn't exist though. When running nox after having deleted all the venvs, I get the following output:

[...]
nox > Running session tests-3.8
nox > Creating virtual environment (virtualenv) using python3.8 in .nox/tests-3-8
nox > poetry install
Creating virtualenv testpkg-pDH_AOha-py3.8 in /home/dominik/.cache/pypoetry/virtualenvs
Installing dependencies from lock file
[...]
  - Installing pytest (5.4.3)
[...]

So it seems pytest gets installed in the wrong venv (the one from poetry; why is this one created in the first place?). The output on the CI server is similar except it doesn't contain the line Creating virtualenv ... after poetry install so here poetry seems to reuse the nox-venv.

Running nox again one my local machine also skips that line but I suppose this is because it silently reuses the already existing poetry-venv.

@Dominik1123
Copy link
Author

I just figured it out (though I don't completely understand why that behavior occurred). Apparently there was a conflict with an active Miniconda environment which was automatically activated through .bashrc (its basically a blank environment). So every time I executed the nox command there was also the Miniconda environment activate. This apparently caused Poetry to create its own venv. After deactivating the Miniconda venv Poetry reuses the nox-venv. Is it because the Miniconda venv shadowed the Nox venv and Poetry realized this and thus created its own venv?

@cjolowicz
Copy link
Owner

cjolowicz commented Jun 16, 2020

Glad you fixed it!

Poetry detects that it is running in a Conda environment via the CONDA_PREFIX environment variable. It will ignore this environment if CONDA_DEFAULT_ENV is "base", to avoid polluting Conda's global "base" env, since most users have it activated all the time. When this happens, it won't look if it is also running in an activated virtual environment.

https://github.com/python-poetry/poetry/blob/1d64e1c75cfd03d8f98533645a7815f0dbcaf421/poetry/utils/env.py#L318-L325

Well, at least that's my impression of what happened here. Not able to test this right now.

@Dominik1123
Copy link
Author

Thanks for sharing that part of the source code, it seems relevant indeed (it was the "base" environment for me).

However I still don't understand the difference between the two cases, since both times in_venv results to be False. I put the various os.environ.get inside one of my nox sessions and this is what I get:

# Miniconda "base" active
env_prefix = "/home/dominik/miniconda3"
conda_env_name = "base"
in_venv = env_prefix is not None and conda_env_name != "base"
assert not in_venv

# Miniconda "base" deactivated
env_prefix = None
conda_env_name = None
in_venv = env_prefix is not None and conda_env_name != "base"
assert not in_venv

So from there on it continues to that branch where I cannot spot a difference between the two cases anymore.

I always assumed that the Nox venv would shadow the Conda venv, and the explicit check for "base" also suggests this (i.e. the base environment is not considered an environment, that's also what the comments imply).

I think I'll open an issue at Poetry since it seems to be related to their venv checks.

@cjolowicz
Copy link
Owner

cjolowicz commented Jun 16, 2020

Well, the os.environ.get in your Nox session won't see VIRTUAL_ENV because it is only set by session.run for the process it spawns, e.g. poetry or pytest.

From what I can tell, Poetry's behavior does seem to be at fault though: It should use VIRTUAL_ENV if it is set, no matter if there is an activated Conda base environment, or not. There might already be an issue for that, I haven't looked.

@Dominik1123
Copy link
Author

You are right, I checked with a separate script which I invoked via session.run and the variables are set as follows:

# Miniconda "base" active
env_prefix = "/home/dominik/Projects/testpkg/.nox/test"
conda_env_name = "base"

# Miniconda "base" inactive
env_prefix = "/home/dominik/Projects/testpkg/.nox/test"
conda_env_name = None

So in the first case this creates a false-positive for not in_venv.

@Dominik1123
Copy link
Author

I created a corresponding issue.

@joezilla86
Copy link

I'm having this same issue where it appears Nox is using the Poetry virtualenv, but I don't think I have the same root cause becuase I am not using miniconda (or any conda). I have been following along with Hypermodern Python using two different computers both setup in WSL from scratch exactly the same way. One works great and the other doesn't. Not sure why they are different.

To answer the questions posed above:
I'm using Ubuntu 20.04 LTS
.nox/... does not exist
N/A
poetry config --list returns

cache-dir = "/home/joe/.cache/pypoetry"
experimental.new-installer = true
virtualenvs.create = true
virtualenvs.in-project = null
virtualenvs.path = "{cache-dir}/virtualenvs" # /home/joe/.cache/pypoetry/virtualenvs

@cjolowicz
Copy link
Owner

cjolowicz commented Oct 18, 2020

Thanks for reporting your issue!

Can you post the following?

  • poetry version
  • nox version
  • output of "nox --verbose" in your project
  • link to your repo, or gist with your noxfile

@joezilla86
Copy link

Your question helped me find the problem. The nox version was 2019.5.30 which did not match what my other computer was using. I ended up actually reinstalling my Ubuntu instance (running as WSL on Windows 10) and have been worked through the same initialization steps again. I realized that the installation of nox didn't quite work as expected. Running "pip install --user --upgrade nox" did complete the install, but then the nox keyword would return

Command 'nox' not found, but can be installed with:
sudo apt install nox

So I realized I had installed via sudo which installed the 2019.5.30 version. The underlying issue is that using the pip method to install nox did not add it to PATH, so I just needed to add

export PATH="$HOME/.local/bin:$PATH" 

to my .bashrc file and reload the shell. Now it seems to work as expected.

@darosio
Copy link

darosio commented Feb 10, 2022

$ poetry config virtualenvs.in-project 1

Solves this issue.
#101 (comment)

@oneextrafact
Copy link

For people arriving here in 2022, I've found that I can get it to work if I execute nox within a poetry shell.

@laclouis5
Copy link

I'm not able to run Nox without the warning Warning: pytest is not installed into the virtualenv, it is located at /Users/<username>/Library/Caches/pypoetry/virtualenvs/testproj-xgPlhPPj-py3.11/bin/pytest. This might cause issues! Pass external=True into run() to silence this message..

My Nox file is:

import nox

@nox.session(python=["3.8", "3.9", "3.10", "3.11"])
def tests(session: nox.Session) -> None:
    session.run("poetry", "shell", external=True)  # Same warning without this line
    session.run("poetry", "install", external=True)
    session.run("pytest")

I'm using Poetry to manage the project which is set up with something like:

poetry new testproj
cd testproj
poetry add --group dev nox pytest
poetry env use python3.11
poetry install
poetry shell

My Python installations are managed through PyEnv and I have a Conda base environment activated at all time to avoid messing up the system Python. I tried to deactivate the Conda base environment but this yields the same warning.

I know I can bypass the issue by installing the project using pip like this:

@nox.session(python=["3.8", "3.9", "3.10", "3.11"])
def tests(session: nox.Session) -> None:
    session.install(".")
    session.install("pytest")
    session.run("pytest")

This works great excepted that it duplicates the development requirements in two places: in the Nox file and in the Project.toml one. However, I think that this last formulation is a better testing setting since the end user would install the package through pip anyway (also, this may be simpler for CI workflows, removing the Poetry install requirement).

Should I stick with this last solution or is there a better alternative for Poetry?

@callmephilip
Copy link

as per @oneextrafact, the following works

poetry run nox

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants