diff --git a/docs/basics.rst b/docs/basics.rst index 9c222eb4..a38d3403 100644 --- a/docs/basics.rst +++ b/docs/basics.rst @@ -188,6 +188,9 @@ To tell pipenv to install a specific version of a library, the usage is simple:: This will update your ``Pipfile`` to reflect this requirement, automatically. +For other version specifiers, see `the relevant section of PEP-440`_. + +.. _`the relevant section of PEP-440`: https://www.python.org/dev/peps/pep-0440/#version-specifiers> ☤ Specifying Versions of Python ------------------------------- @@ -326,6 +329,7 @@ You should do this for your shell too, in your ``~/.profile`` or ``~/.bashrc`` o .. note:: The shell launched in interactive mode. This means that if your shell reads its configuration from a specific file for interactive mode (e.g. bash by default looks for a ``~/.bashrc`` configuration file for interactive mode), then you'll need to modify (or create) this file. +If you experience issues with ``$ pipenv shell``, just check the ``PIPENV_SHELL`` environment variable, which ``$ pipenv shell`` will use if available. For detail, see :ref:`configuration-with-environment-variables`. ☤ A Note about VCS Dependencies ------------------------------- @@ -362,3 +366,4 @@ production environments for reproducible builds. This will include all hashes, however (which is great!). To get a ``requirements.txt`` without hashes, use ``$ pipenv run pip freeze``. +.. _configuration-with-environment-variables:https://docs.pipenv.org/advanced/#configuration-with-environment-variables diff --git a/docs/requirements.txt b/docs/requirements.txt index 92110aa5..71c58644 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -7,7 +7,7 @@ docutils==0.14 first==2.0.1 idna==2.6 imagesize==0.7.1 -Jinja2==2.9.6 +Jinja2==2.10 MarkupSafe==1.0 pbr==3.1.1 pip-tools==1.9.0 diff --git a/news/2671.doc b/news/2671.doc new file mode 100644 index 00000000..859cc377 --- /dev/null +++ b/news/2671.doc @@ -0,0 +1 @@ +Added additional information about troubleshooting ``pipenv shell`` by using the the ``$PIPENV_SHELL`` environment variable. diff --git a/news/2674.doc b/news/2674.doc new file mode 100644 index 00000000..33fbd49e --- /dev/null +++ b/news/2674.doc @@ -0,0 +1 @@ +Added a link to ``PEP-440`` version specifiers in the documentation for additional detail. diff --git a/news/2732.bugfix b/news/2732.bugfix new file mode 100644 index 00000000..d1cf591b --- /dev/null +++ b/news/2732.bugfix @@ -0,0 +1 @@ +Correctly pass `verbose` and `debug` flags to the resolver subprocess so it generates appropriate output. This also resolves a bug introduced by the fix to #2527. diff --git a/pipenv/core.py b/pipenv/core.py index db60ee94..31adf699 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -550,15 +550,19 @@ def ensure_project( ): click.echo( "{0}: Your Pipfile requires {1} {2}, " - "but you are using {3} ({4}). Running" - "{5} and rebuilding the virtual environment" - "may resolve the issue".format( + "but you are using {3} ({4}).".format( crayons.red("Warning", bold=True), crayons.normal("python_version", bold=True), crayons.blue(project.required_python_version), crayons.blue(python_version(path_to_python)), crayons.green(shorten_path(path_to_python)), - crayons.green("`pipenv --rm`"), + ), + err=True, + ) + click.echo( + " {0} and rebuilding the virtual environment " + "may resolve the issue.".format( + crayons.green("$ pipenv --rm"), ), err=True, ) diff --git a/pipenv/environments.py b/pipenv/environments.py index 0e21ca3b..b6633f39 100644 --- a/pipenv/environments.py +++ b/pipenv/environments.py @@ -95,7 +95,7 @@ PIPENV_MAX_RETRIES = int(os.environ.get( )) """Specify how many retries Pipenv should attempt for network requests. -Default is 0. Aautomatically set to 1 on CI environments for robust testing. +Default is 0. Automatically set to 1 on CI environments for robust testing. """ PIPENV_MAX_ROUNDS = int(os.environ.get("PIPENV_MAX_ROUNDS", "16")) diff --git a/pipenv/resolver.py b/pipenv/resolver.py index e4c9b09b..6526d990 100644 --- a/pipenv/resolver.py +++ b/pipenv/resolver.py @@ -22,6 +22,7 @@ def which(*args, **kwargs): def main(): do_pre = "--pre" in " ".join(sys.argv) do_clear = "--clear" in " ".join(sys.argv) + is_verbose = "--verbose" in " ".join(sys.argv) is_debug = "--debug" in " ".join(sys.argv) system = "--system" in " ".join(sys.argv) new_sys_argv = [] @@ -35,11 +36,17 @@ def main(): os.environ["PIP_PYTHON_VERSION"] = ".".join([str(s) for s in sys.version_info[:3]]) os.environ["PIP_PYTHON_PATH"] = sys.executable - if int(os.environ.get("PIPENV_VERBOSITY", 0)) > 0: - logging.getLogger("notpip").setLevel(logging.INFO) + + verbosity = int(os.environ.get("PIPENV_VERBOSITY", 0)) if is_debug: - # Shit's getting real at this point. + verbosity = max(verbosity, 2) + elif is_verbose: + verbosity = max(verbosity, 1) + if verbosity > 1: # Shit's getting real at this point. logging.getLogger("notpip").setLevel(logging.DEBUG) + elif verbosity > 0: + logging.getLogger("notpip").setLevel(logging.INFO) + if "PIPENV_PACKAGES" in os.environ: packages = os.environ["PIPENV_PACKAGES"].strip().split("\n") else: diff --git a/pipenv/utils.py b/pipenv/utils.py index 1f0f4ef4..9d0427b7 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -352,11 +352,10 @@ def venv_resolve_deps( if not deps: return [] resolver = escape_grouped_arguments(resolver.__file__.rstrip("co")) - cmd = "{0} {1} {2} {3} {4} {5}".format( + cmd = "{0} {1} {2} {3} {4}".format( escape_grouped_arguments(which("python", allow_global=allow_global)), resolver, "--pre" if pre else "", - "--verbose" if (environments.is_verbose()) else "", "--clear" if clear else "", "--system" if allow_global else "", ) @@ -364,6 +363,7 @@ def venv_resolve_deps( os.environ["PIPENV_PACKAGES"] = "\n".join(deps) if pypi_mirror: os.environ["PIPENV_PYPI_MIRROR"] = str(pypi_mirror) + os.environ["PIPENV_VERBOSITY"] = str(environments.PIPENV_VERBOSITY) c = delegator.run(cmd, block=True) try: assert c.return_code == 0 diff --git a/tests/integration/conftest.py b/tests/integration/conftest.py index 8a8aebe6..9cb4b7b1 100644 --- a/tests/integration/conftest.py +++ b/tests/integration/conftest.py @@ -28,7 +28,30 @@ def check_internet(): return True +def check_github_ssh(): + res = False + try: + # `ssh -T git@github.com` will return successfully with return_code==1 + # and message 'Hi ! You've successfully authenticated, but + # GitHub does not provide shell access.' if ssh keys are available and + # registered with GitHub. Otherwise, the command will fail with + # return_code=255 and say 'Permission denied (publickey).' + c = delegator.run('ssh -T git@github.com') + res = True if c.return_code == 1 else False + except Exception: + pass + if not res: + warnings.warn( + 'Cannot connect to GitHub via SSH', ResourceWarning + ) + warnings.warn( + 'Will skip tests requiring SSH access to GitHub', ResourceWarning + ) + return res + + WE_HAVE_INTERNET = check_internet() +WE_HAVE_GITHUB_SSH_KEYS = check_github_ssh() TESTS_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) PYPI_VENDOR_DIR = os.path.join(TESTS_ROOT, 'pypi') @@ -38,6 +61,8 @@ prepare_pypi_packages(PYPI_VENDOR_DIR) def pytest_runtest_setup(item): if item.get_marker('needs_internet') is not None and not WE_HAVE_INTERNET: pytest.skip('requires internet') + if item.get_marker('needs_github_ssh') is not None and not WE_HAVE_GITHUB_SSH_KEYS: + pytest.skip('requires github ssh') class _PipenvInstance(object): diff --git a/tests/integration/test_install_twists.py b/tests/integration/test_install_twists.py index d2cdd9aa..8653e2c0 100644 --- a/tests/integration/test_install_twists.py +++ b/tests/integration/test_install_twists.py @@ -57,6 +57,66 @@ zip_safe=False assert "six" in p.lockfile["default"] +@pytest.mark.install +@pytest.mark.local +@pytest.mark.needs_internet +@flaky +class TestDependencyLinks(object): + """Ensure dependency_links are parsed and installed. + + This is needed for private repo dependencies. + """ + + @staticmethod + def helper_dependency_links_install_make_setup(pipenv_instance, deplink): + setup_py = os.path.join(pipenv_instance.path, "setup.py") + with open(setup_py, "w") as fh: + contents = """ +from setuptools import setup + +setup( + name='testdeplinks', + version='0.1', + packages=[], + install_requires=[ + 'test-private-dependency' + ], + dependency_links=[ + '{0}' + ] +) + """.strip().format(deplink) + fh.write(contents) + + @staticmethod + def helper_dependency_links_install_test(pipenv_instance, deplink): + TestDependencyLinks.helper_dependency_links_install_make_setup(pipenv_instance, deplink) + c = pipenv_instance.pipenv("install -v -e .") + assert c.return_code == 0 + assert "test-private-dependency" in pipenv_instance.lockfile["default"] + assert "version" in pipenv_instance.lockfile["default"]["test-private-dependency"] + assert "0.1" in pipenv_instance.lockfile["default"]["test-private-dependency"]["version"] + + def test_https_dependency_links_install(self, PipenvInstance, pypi): + """Ensure dependency_links are parsed and installed (needed for private repo dependencies). + """ + with temp_environ(), PipenvInstance(pypi=pypi, chdir=True) as p: + os.environ['PIP_PROCESS_DEPENDENCY_LINKS'] = '1' + TestDependencyLinks.helper_dependency_links_install_test( + p, + 'git+https://github.com/atzannes/test-private-dependency@v0.1#egg=test-private-dependency-v0.1' + ) + + @pytest.mark.needs_github_ssh + def test_ssh_dependency_links_install(self, PipenvInstance, pypi): + with temp_environ(), PipenvInstance(pypi=pypi, chdir=True) as p: + os.environ['PIP_PROCESS_DEPENDENCY_LINKS'] = '1' + TestDependencyLinks.helper_dependency_links_install_test( + p, + 'git+ssh://git@github.com/atzannes/test-private-dependency@v0.1#egg=test-private-dependency-v0.1' + ) + + @pytest.mark.e @pytest.mark.install @pytest.mark.skip(reason="this doesn't work on windows") diff --git a/tests/integration/test_install_uri.py b/tests/integration/test_install_uri.py index 24f7996f..c51db6ad 100644 --- a/tests/integration/test_install_uri.py +++ b/tests/integration/test_install_uri.py @@ -25,6 +25,39 @@ def test_basic_vcs_install(PipenvInstance, pip_src_dir, pypi): assert "gitdb2" in p.lockfile["default"] +@pytest.mark.vcs +@pytest.mark.install +@pytest.mark.needs_internet +@flaky +def test_git_vcs_install(PipenvInstance, pip_src_dir, pypi): + with PipenvInstance(pypi=pypi, chdir=True) as p: + c = p.pipenv("install git+git://github.com/benjaminp/six.git@1.11.0#egg=six") + assert c.return_code == 0 + assert "six" in p.pipfile["packages"] + assert "git" in p.pipfile["packages"]["six"] + assert p.lockfile["default"]["six"] == { + "git": "git://github.com/benjaminp/six.git", + "ref": "15e31431af97e5e64b80af0a3f598d382bcdd49a", + } + + +@pytest.mark.vcs +@pytest.mark.install +@pytest.mark.needs_github_ssh +@pytest.mark.needs_internet +@flaky +def test_ssh_vcs_install(PipenvInstance, pip_src_dir, pypi): + with PipenvInstance(pypi=pypi, chdir=True) as p: + c = p.pipenv("install git+ssh://git@github.com/benjaminp/six.git@1.11.0#egg=six") + assert c.return_code == 0 + assert "six" in p.pipfile["packages"] + assert "git" in p.pipfile["packages"]["six"] + assert p.lockfile["default"]["six"] == { + "git": "ssh://git@github.com/benjaminp/six.git", + "ref": "15e31431af97e5e64b80af0a3f598d382bcdd49a", + } + + @pytest.mark.files @pytest.mark.urls @pytest.mark.needs_internet