diff --git a/pipenv/core.py b/pipenv/core.py index 22ef34bc..e229db3b 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -1047,12 +1047,8 @@ def do_lock( err=True, ) - deps = convert_deps_to_pip( - packages, project, r=False, include_index=True - ) - # Mutates the lockfile venv_resolve_deps( - deps, + packages, which=which, project=project, dev=is_dev, @@ -1296,6 +1292,10 @@ def pip_install( crayons.normal("Installing {0!r}".format(requirement.name), bold=True), err=True, ) + + if requirement: + ignore_hashes = True if not requirement.hashes else ignore_hashes + # Create files for hash mode. if write_to_tmpfile: if not requirements_dir: @@ -1305,14 +1305,13 @@ def pip_install( prefix="pipenv-", suffix="-requirement.txt", dir=requirements_dir, delete=False ) - f.write(vistir.misc.to_bytes(requirement.as_line())) + line = requirement.as_line(include_hashes=not ignore_hashes) + f.write(vistir.misc.to_bytes(line)) r = f.name f.close() if requirement and requirement.vcs: # Install dependencies when a package is a non-editable VCS dependency. - if not requirement.editable: - no_deps = False # Don't specify a source directory when using --system. if not allow_global and ("PIP_SRC" not in os.environ): src.extend(["--src", "{0}".format(project.virtualenv_src_location)]) @@ -1363,6 +1362,7 @@ def pip_install( # Install dependencies when a package is a VCS dependency. if requirement and requirement.vcs: + ignore_hashes = True # Don't specify a source directory when using --system. src_dir = None if "PIP_SRC" in os.environ: @@ -1421,17 +1421,19 @@ def pip_install( install_reqs = requirement.as_line(**line_kwargs) if requirement.editable and install_reqs[0].startswith("-e "): req, install_reqs = install_reqs[0], install_reqs[1:] + possible_hashes = install_reqs[:] editable_opt, req = req.split(" ", 1) install_reqs = [editable_opt, req] + install_reqs - if not any(item.startswith("--hash") for item in install_reqs): - ignore_hashes = True + + # hashes must be passed via a file + ignore_hashes = True elif r: install_reqs = ["-r", r] with open(r) as f: if "--hash" not in f.read(): ignore_hashes = True else: - ignore_hashes = True if not requirement.hashes else False + ignore_hashes = True install_reqs = requirement.as_line(as_list=True, include_hashes=not ignore_hashes) if not requirement.markers: install_reqs = [escape_cmd(r) for r in install_reqs] @@ -1454,8 +1456,10 @@ def pip_install( if not ignore_hashes: pip_command.append("--require-hashes") if not use_pep517: + from .vendor.packaging.version import parse as parse_version pip_command.append("--no-build-isolation") - pip_command.append("--no-use-pep517") + if project.environment.pip_version >= parse_version("19.0"): + pip_command.append("--no-use-pep517") if environments.is_verbose(): click.echo("$ {0}".format(pip_command), err=True) cache_dir = vistir.compat.Path(PIPENV_CACHE_DIR) diff --git a/pipenv/environment.py b/pipenv/environment.py index 9e75b7d8..602859e2 100644 --- a/pipenv/environment.py +++ b/pipenv/environment.py @@ -188,12 +188,14 @@ class Environment(object): @cached_property def sys_path(self): - """The system path inside the environment + """ + The system path inside the environment :return: The :data:`sys.path` from the environment :rtype: list """ + from .vendor.vistir.compat import JSONDecodeError current_executable = vistir.compat.Path(sys.executable).as_posix() if not self.python or self.python == current_executable: return sys.path @@ -201,12 +203,16 @@ class Environment(object): return sys.path cmd_args = [self.python, "-c", "import json, sys; print(json.dumps(sys.path))"] path, _ = vistir.misc.run(cmd_args, return_object=False, nospin=True, block=True, combine_stderr=False, write_to_stdout=False) - path = json.loads(path.strip()) + try: + path = json.loads(path.strip()) + except JSONDecodeError: + path = sys.path return path @cached_property def sys_prefix(self): - """The prefix run inside the context of the environment + """ + The prefix run inside the context of the environment :return: The python prefix inside the environment :rtype: :data:`sys.prefix` @@ -241,8 +247,23 @@ class Environment(object): return "purelib", purelib return "platlib", self.paths["platlib"] + @property + def pip_version(self): + """ + Get the pip version in the environment. Useful for knowing which args we can use + when installing. + """ + from .vendor.packaging.version import parse as parse_version + pip = next(iter( + pkg for pkg in self.get_installed_packages() if pkg.key == "pip" + ), None) + if pip is not None: + pip_version = parse_version(pip.version) + return parse_version("18.0") + def get_distributions(self): - """Retrives the distributions installed on the library path of the environment + """ + Retrives the distributions installed on the library path of the environment :return: A set of distributions found on the library path :rtype: iterator diff --git a/pipenv/project.py b/pipenv/project.py index 8e209638..eb802cbb 100644 --- a/pipenv/project.py +++ b/pipenv/project.py @@ -537,7 +537,8 @@ class Project(object): @property def build_requires(self): - return self._build_system.get("requires", []) + return self._build_system.get("requires", ["setuptools>=40.8.0", "wheel"]) + @property def build_backend(self): diff --git a/pipenv/utils.py b/pipenv/utils.py index aec4ee96..ef290e54 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -900,6 +900,7 @@ def venv_resolve_deps( from .vendor.vistir.compat import Path, JSONDecodeError from .vendor.vistir.path import create_tracked_tempdir from . import resolver + from ._compat import decode_for_output import json results = [] @@ -908,12 +909,7 @@ def venv_resolve_deps( if not deps: if not project.pipfile_exists: return None - # This is a requirementslib pipfile instance which provides `Requirement` instances - # rather than simply locked dependencies in a lockfile format - deps = convert_deps_to_pip( - project.parsed_pipfile.get(pipfile_section, {}), project=project, - r=False, include_index=True - ) + deps = project.parsed_pipfile.get(pipfile_section, {}) if not deps: return None @@ -922,7 +918,6 @@ def venv_resolve_deps( if not lockfile: lockfile = project._lockfile req_dir = create_tracked_tempdir(prefix="pipenv", suffix="requirements") - constraints = set(deps) cmd = [ which("python", allow_global=allow_global), Path(resolver.__file__.rstrip("co")).as_posix() @@ -933,16 +928,28 @@ def venv_resolve_deps( cmd.append("--clear") if allow_global: cmd.append("--system") + if dev: + cmd.append("--dev") with temp_environ(): os.environ.update({fs_str(k): fs_str(val) for k, val in os.environ.items()}) - os.environ["PIPENV_PACKAGES"] = str("\n".join(constraints)) if pypi_mirror: os.environ["PIPENV_PYPI_MIRROR"] = str(pypi_mirror) os.environ["PIPENV_VERBOSITY"] = str(environments.PIPENV_VERBOSITY) os.environ["PIPENV_REQ_DIR"] = fs_str(req_dir) os.environ["PIP_NO_INPUT"] = fs_str("1") os.environ["PIPENV_SITE_DIR"] = get_pipenv_sitedir() - with create_spinner(text=fs_str("Locking...")) as sp: + with create_spinner(text=decode_for_output("Locking...")) as sp: + # This conversion is somewhat slow on local and file-type requirements since + # we now download those requirements / make temporary folders to perform + # dependency resolution on them, so we are including this step inside the + # spinner context manager for the UX improvement + sp.write(decode_for_output("Building requirements...")) + deps = convert_deps_to_pip( + deps, project, r=False, include_index=True + ) + constraints = set(deps) + os.environ["PIPENV_PACKAGES"] = str("\n".join(constraints)) + sp.write(decode_for_output("Resolving dependencies...")) c = resolve(cmd, sp) results = c.out.strip() sp.green.ok(environments.PIPENV_SPINNER_OK_TEXT.format("Success!")) diff --git a/pipenv/vendor/requirementslib/models/requirements.py b/pipenv/vendor/requirementslib/models/requirements.py index d6beb085..d57e4fff 100644 --- a/pipenv/vendor/requirementslib/models/requirements.py +++ b/pipenv/vendor/requirementslib/models/requirements.py @@ -2078,16 +2078,14 @@ class VCSRequirement(FileRequirement): path = pip_shims.shims.url_to_path(self.uri) if path and os.path.exists(path): checkout_dir = os.path.abspath(path) - return vistir.compat.fs_encode(checkout_dir) + return checkout_dir if src_dir is not None: checkout_dir = os.path.join( os.path.abspath(src_dir), self.name ) - mkdir_p(vistir.compat.fs_encode(src_dir)) + mkdir_p(src_dir) return checkout_dir - return vistir.compat.fs_encode( - os.path.join(create_tracked_tempdir(prefix="requirementslib"), self.name) - ) + return os.path.join(create_tracked_tempdir(prefix="requirementslib"), self.name) def get_vcs_repo(self, src_dir=None, checkout_dir=None): # type: (Optional[STRING_TYPE], STRING_TYPE) -> VCSRepository