diff --git a/news/2763.bugfix.rst b/news/2763.bugfix.rst new file mode 100644 index 00000000..f7799d4d --- /dev/null +++ b/news/2763.bugfix.rst @@ -0,0 +1,2 @@ +Pipenv will now ignore ``.venv`` in the project when ``PIPENV_VENV_IN_PROJECT`` variable is False. +Unset variable maintains the existing behavior of preferring to use the project's ``.venv`` should it exist. diff --git a/news/4992.process.rst b/news/4992.process.rst deleted file mode 100644 index d9d82af5..00000000 --- a/news/4992.process.rst +++ /dev/null @@ -1 +0,0 @@ -Internal to pipenv, the utils.py was split into a utils module with unused code removed. diff --git a/pipenv/environments.py b/pipenv/environments.py index 41a35659..3eff6485 100644 --- a/pipenv/environments.py +++ b/pipenv/environments.py @@ -297,10 +297,18 @@ class Setting: Default is 120 seconds, an arbitrary number that seems to work. """ - self.PIPENV_VENV_IN_PROJECT = bool(os.environ.get("PIPENV_VENV_IN_PROJECT")) - """If set, creates ``.venv`` in your project directory. - - Default is to create new virtual environments in a global location. + self.PIPENV_VENV_IN_PROJECT = os.environ.get("PIPENV_VENV_IN_PROJECT") + if self.PIPENV_VENV_IN_PROJECT is not None: + if self.PIPENV_VENV_IN_PROJECT.lower() in _true_values: + self.PIPENV_VENV_IN_PROJECT = True + elif self.PIPENV_VENV_IN_PROJECT.lower() in _false_values: + self.PIPENV_VENV_IN_PROJECT = False + else: + self.PIPENV_VENV_IN_PROJECT = None + """ When set True, will create or use the ``.venv`` in your project directory. + When Set False, will ignore the .venv in your project directory even if it exists. + Default is None will use the .venv of project directory should it exist, otherwise + will create new virtual environments in a global location. """ self.PIPENV_VERBOSE = bool(os.environ.get("PIPENV_VERBOSE")) diff --git a/pipenv/project.py b/pipenv/project.py index d9732652..26e3aa40 100644 --- a/pipenv/project.py +++ b/pipenv/project.py @@ -242,6 +242,8 @@ class Project: def is_venv_in_project(self): # type: () -> bool + if self.s.PIPENV_VENV_IN_PROJECT is False: + return False return self.s.PIPENV_VENV_IN_PROJECT or ( self.project_directory and os.path.isdir(os.path.join(self.project_directory, ".venv")) @@ -288,7 +290,7 @@ class Project: return str(get_workon_home().joinpath(self.virtualenv_name)) # If content looks like a path, use it as a relative path. - # Otherwise use directory named after content in WORKON_HOME. + # Otherwise, use directory named after content in WORKON_HOME. if looks_like_dir(name): path = Path(self.project_directory, name) return path.absolute().as_posix() diff --git a/tests/integration/test_dot_venv.py b/tests/integration/test_dot_venv.py index 922bc49d..053a5dda 100644 --- a/tests/integration/test_dot_venv.py +++ b/tests/integration/test_dot_venv.py @@ -7,13 +7,15 @@ from tempfile import TemporaryDirectory import pytest +from pipenv.environments import _true_values, _false_values from pipenv.utils.shell import normalize_drive, temp_environ @pytest.mark.dotvenv -def test_venv_in_project(PipenvInstance): +@pytest.mark.parametrize("true_value", _true_values) +def test_venv_in_project(true_value, PipenvInstance): with temp_environ(): - os.environ['PIPENV_VENV_IN_PROJECT'] = '1' + os.environ['PIPENV_VENV_IN_PROJECT'] = true_value with PipenvInstance() as p: c = p.pipenv('install requests') assert c.returncode == 0 @@ -21,10 +23,38 @@ def test_venv_in_project(PipenvInstance): @pytest.mark.dotvenv -def test_venv_at_project_root(PipenvInstance): +@pytest.mark.parametrize("false_value", _false_values) +def test_venv_in_project_disabled_ignores_venv(false_value, PipenvInstance): + venv_name = "my_project" + with temp_environ(): + os.environ['PIPENV_VENV_IN_PROJECT'] = false_value + with PipenvInstance() as p: + file_path = os.path.join(p.path, '.venv') + with open(file_path, 'w') as f: + f.write(venv_name) + + with temp_environ(), TemporaryDirectory( + prefix='pipenv-', suffix='temp_workon_home' + ) as workon_home: + os.environ['WORKON_HOME'] = workon_home + c = p.pipenv('install requests') + assert c.returncode == 0 + c = p.pipenv('--venv') + assert c.returncode == 0 + venv_loc = Path(c.stdout.strip()).absolute() + assert venv_loc.exists() + assert venv_loc.joinpath('.project').exists() + venv_path = normalize_drive(venv_loc.as_posix()) + venv_expected_path = Path(workon_home).joinpath(venv_name).absolute().as_posix() + assert venv_path == normalize_drive(venv_expected_path) + + +@pytest.mark.dotvenv +@pytest.mark.parametrize("true_value", _true_values) +def test_venv_at_project_root(true_value, PipenvInstance): with temp_environ(): with PipenvInstance(chdir=True) as p: - os.environ['PIPENV_VENV_IN_PROJECT'] = '1' + os.environ['PIPENV_VENV_IN_PROJECT'] = true_value c = p.pipenv('install') assert c.returncode == 0 assert normalize_drive(p.path) in p.pipenv('--venv').stdout @@ -80,7 +110,7 @@ def test_venv_file(venv_name, PipenvInstance): @pytest.mark.dotvenv def test_empty_venv_file(PipenvInstance): - """Tests virtualenv creation when a empty .venv file exists at the project root + """Tests virtualenv creation when an empty .venv file exists at the project root """ with PipenvInstance(chdir=True) as p: file_path = os.path.join(p.path, '.venv') @@ -109,9 +139,8 @@ def test_empty_venv_file(PipenvInstance): @pytest.mark.dotvenv -def test_venv_file_with_path(PipenvInstance): - """Tests virtualenv creation when a .venv file exists at the project root - and contains an absolute path. +def test_venv_in_project_default_when_venv_exists(PipenvInstance): + """Tests virtualenv creation when a .venv file exists at the project root. """ with temp_environ(), PipenvInstance(chdir=True) as p: with TemporaryDirectory( diff --git a/tests/integration/test_install_basic.py b/tests/integration/test_install_basic.py index 177a71a5..2b1b3444 100644 --- a/tests/integration/test_install_basic.py +++ b/tests/integration/test_install_basic.py @@ -405,8 +405,6 @@ def test_install_venv_project_directory(PipenvInstance): prefix="pipenv-", suffix="temp_workon_home" ) as workon_home: os.environ["WORKON_HOME"] = workon_home - if "PIPENV_VENV_IN_PROJECT" in os.environ: - del os.environ["PIPENV_VENV_IN_PROJECT"] c = p.pipenv("install six") assert c.returncode == 0 diff --git a/tests/integration/test_pipenv.py b/tests/integration/test_pipenv.py index 39c5853d..ced2d2c6 100644 --- a/tests/integration/test_pipenv.py +++ b/tests/integration/test_pipenv.py @@ -92,14 +92,9 @@ def test_proper_names_unamanged_virtualenv(PipenvInstance): def test_directory_with_leading_dash(raw_venv, PipenvInstance): with temp_environ(): with PipenvInstance(chdir=True, venv_in_project=False, name="-project-with-dash") as p: - if "PIPENV_VENV_IN_PROJECT" in os.environ: - del os.environ['PIPENV_VENV_IN_PROJECT'] c = p.pipenv('run pip freeze') assert c.returncode == 0 c = p.pipenv('--venv') assert c.returncode == 0 venv_path = c.stdout.strip() assert os.path.isdir(venv_path) - # Manually clean up environment, since PipenvInstance assumes that - # the virutalenv is in the project directory. - p.pipenv('--rm')