diff --git a/.env b/.env deleted file mode 100644 index d77e8f12a7..0000000000 --- a/.env +++ /dev/null @@ -1 +0,0 @@ -HELLO=WORLD diff --git a/news/5010.bugfix.md b/news/5010.bugfix.md new file mode 100644 index 0000000000..e532226ba7 --- /dev/null +++ b/news/5010.bugfix.md @@ -0,0 +1 @@ +Environment variables were not being loaded when the `--quiet` flag was set diff --git a/news/5011.trivial.rst b/news/5011.trivial.rst new file mode 100644 index 0000000000..86b7eebb9c --- /dev/null +++ b/news/5011.trivial.rst @@ -0,0 +1 @@ +Adds missing unit test coverage for recent bug fixes. diff --git a/news/5019.bugfix.rst b/news/5019.bugfix.rst new file mode 100644 index 0000000000..755e5e3f32 --- /dev/null +++ b/news/5019.bugfix.rst @@ -0,0 +1,4 @@ +It would appear that ``requirementslib`` was not fully specifying the subdirectory to ``build_pep517`` and +and when a new version of ``setuptools`` was released, the test ``test_lock_nested_vcs_direct_url`` +broke indicating the Pipfile.lock no longer contained the extra dependencies that should have been resolved. +This regression affected ``pipenv>=2021.11.9`` but has been fixed by a patch to ``requirementslib``. diff --git a/pipenv/core.py b/pipenv/core.py index b1e8d01ed1..ab3033ea0f 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -115,11 +115,12 @@ def load_dot_env(project, as_dict=False, quiet=False): ) if as_dict: return dotenv.dotenv_values(dotenv_file) - elif os.path.isfile(dotenv_file) and not quiet: - click.echo( - crayons.normal(fix_utf8("Loading .env environment variables..."), bold=True), - err=True, - ) + elif os.path.isfile(dotenv_file): + if not quiet: + click.echo( + crayons.normal(fix_utf8("Loading .env environment variables..."), bold=True), + err=True, + ) dotenv.load_dotenv(dotenv_file, override=True) project.s.initialize() diff --git a/pipenv/vendor/requirementslib/models/setup_info.py b/pipenv/vendor/requirementslib/models/setup_info.py index 772a4d8129..dc001f3663 100644 --- a/pipenv/vendor/requirementslib/models/setup_info.py +++ b/pipenv/vendor/requirementslib/models/setup_info.py @@ -13,7 +13,7 @@ from collections.abc import Iterable, Mapping from functools import lru_cache, partial from pathlib import Path -from urllib.parse import urlparse, urlunparse +from urllib.parse import parse_qs, urlparse, urlunparse from weakref import finalize import pipenv.vendor.attr as attr @@ -1230,8 +1230,15 @@ def build_wheel(self): ) ) need_delete = True + + parsed = urlparse(str(self.ireq.link)) + subdir = parse_qs(parsed.fragment).get('subdirectory', []) + if subdir: + directory = f"{self.base_dir}/{subdir[0]}" + else: + directory = self.base_dir result = build_pep517( - self.base_dir, + directory, self.extra_kwargs["build_dir"], config_settings=self.pep517_config, dist_type="wheel", diff --git a/setup.py b/setup.py index 383dc0b5f9..3d56518c26 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ required = [ "pip>=18.0", "certifi", - "setuptools>=36.2.1,<61.0.0", + "setuptools>=36.2.1", "virtualenv-clone>=0.2.5", "virtualenv" ] diff --git a/tasks/vendoring/patches/vendor/requirementslib-subdir-fix.patch b/tasks/vendoring/patches/vendor/requirementslib-subdir-fix.patch new file mode 100644 index 0000000000..41519a0540 --- /dev/null +++ b/tasks/vendoring/patches/vendor/requirementslib-subdir-fix.patch @@ -0,0 +1,30 @@ +diff --git a/pipenv/vendor/requirementslib/models/setup_info.py b/pipenv/vendor/requirementslib/models/setup_info.py +index 772a4d81..dc001f36 100644 +--- a/pipenv/vendor/requirementslib/models/setup_info.py ++++ b/pipenv/vendor/requirementslib/models/setup_info.py +@@ -13,7 +13,7 @@ import sys + from collections.abc import Iterable, Mapping + from functools import lru_cache, partial + from pathlib import Path +-from urllib.parse import urlparse, urlunparse ++from urllib.parse import parse_qs, urlparse, urlunparse + from weakref import finalize + + import pipenv.vendor.attr as attr +@@ -1230,8 +1230,15 @@ build-backend = "{1}" + ) + ) + need_delete = True ++ ++ parsed = urlparse(str(self.ireq.link)) ++ subdir = parse_qs(parsed.fragment).get('subdirectory', []) ++ if subdir: ++ directory = f"{self.base_dir}/{subdir[0]}" ++ else: ++ directory = self.base_dir + result = build_pep517( +- self.base_dir, ++ directory, + self.extra_kwargs["build_dir"], + config_settings=self.pep517_config, + dist_type="wheel", diff --git a/tests/integration/test_cli.py b/tests/integration/test_cli.py index 75655bf576..9cff623ba3 100644 --- a/tests/integration/test_cli.py +++ b/tests/integration/test_cli.py @@ -210,27 +210,6 @@ def test_scripts(PipenvInstance): assert 'which python' in c.stdout -@pytest.mark.cli -def test_scripts_resolve_dot_env_vars(PipenvInstance): - with PipenvInstance() as p: - with open(".env", "w") as f: - contents = """ -HELLO=WORLD - """.strip() - f.write(contents) - - with open(p.pipfile_path, "w") as f: - contents = """ -[scripts] -hello = "echo $HELLO" - """.strip() - f.write(contents) - c = p.pipenv('run hello') - print(c) - print(c.stdout) - assert 'WORLD' in c.stdout - - @pytest.mark.cli def test_help(PipenvInstance): with PipenvInstance() as p: diff --git a/tests/integration/test_lock.py b/tests/integration/test_lock.py index e1235f5401..1ac520cfc3 100644 --- a/tests/integration/test_lock.py +++ b/tests/integration/test_lock.py @@ -804,3 +804,40 @@ def test_default_lock_overwrite_dev_lock(PipenvInstance): assert c.returncode == 0 assert p.lockfile["default"]["click"]["version"] == "==6.7" assert p.lockfile["develop"]["click"]["version"] == "==6.7" + + +@flaky +@pytest.mark.lock +@pytest.mark.install +@pytest.mark.needs_internet +def test_pipenv_respects_package_index_restrictions(PipenvInstance): + with PipenvInstance() as p: + with open(p.pipfile_path, 'w') as f: + contents = """ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[[source]] +url = "{url}/simple" +verify_ssl = true +name = "local" + +[packages] +requests = {requirement} + """.strip().format(url=p.pypi, requirement='{version="*", index="local"}') + f.write(contents) + + c = p.pipenv('lock') + assert c.returncode == 0 + assert 'requests' in p.lockfile['default'] + assert 'idna' in p.lockfile['default'] + assert 'certifi' in p.lockfile['default'] + assert 'urllib3' in p.lockfile['default'] + assert 'chardet' in p.lockfile['default'] + # this is the newest version we have in our private pypi (but pypi.org has 2.27.1 at present) + expected_result = {'hashes': ['sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1', + 'sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a'], + 'index': 'local', 'version': '==2.19.1'} + assert p.lockfile['default']['requests'] == expected_result diff --git a/tests/integration/test_run.py b/tests/integration/test_run.py index b0b8bbc050..9debdfbdfe 100644 --- a/tests/integration/test_run.py +++ b/tests/integration/test_run.py @@ -82,3 +82,48 @@ def test_run_with_usr_env_shebang(PipenvInstance): project = Project() lines = [line.strip() for line in c.stdout.splitlines()] assert all(line == project.virtualenv_location for line in lines) + + +@pytest.mark.run +@pytest.mark.parametrize('quiet', [True, False]) +def test_scripts_resolve_dot_env_vars(quiet, PipenvInstance): + with PipenvInstance() as p: + with open(".env", "w") as f: + contents = """ +HELLO_VAR=WORLD + """.strip() + f.write(contents) + + with open(p.pipfile_path, "w") as f: + contents = """ +[scripts] +hello = "echo $HELLO_VAR" + """.strip() + f.write(contents) + if quiet: + c = p.pipenv('run --quiet hello') + else: + c = p.pipenv('run hello') + assert c.returncode == 0 + assert 'WORLD\n' == c.stdout + + +@pytest.mark.run +@pytest.mark.parametrize('quiet', [True, False]) +def test_pipenv_run_pip_freeze_has_expected_output(quiet, PipenvInstance): + with PipenvInstance() as p: + with open(p.pipfile_path, 'w') as f: + contents = """ + [packages] + requests = "==2.14.0" + """.strip() + f.write(contents) + c = p.pipenv('install') + assert c.returncode == 0 + + if quiet: + c = p.pipenv('run --quiet pip freeze') + else: + c = p.pipenv('run pip freeze') + assert c.returncode == 0 + assert 'requests==2.14.0\n' == c.stdout