diff --git a/pipenv/utils.py b/pipenv/utils.py index 62a2984a..2cd58ae5 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -26,6 +26,10 @@ def shellquote(s): return "'" + s.replace("'", "'\\''") + "'" +def clean_pkg_version(version): + return six.u(pep440_version(str(version))).replace('==', '') + + def resolve_deps(deps, sources=None, verbose=False, hashes=False): constraints = [] @@ -53,10 +57,20 @@ def resolve_deps(deps, sources=None, verbose=False, hashes=False): r = Resolver(constraints=constraints, repository=pypi) results = [] + # pre-resolve instead of iterating to avoid asking pypi for hashes of editable packages + resolved_tree = r.resolve() + hashes = r.resolve_hashes((req for req in resolved_tree if not req.editable)) + # convert to a dictionary indexed by package names instead of install req objects + resolved_hashes = {} + for req, pypi_hash in hashes.items(): + resolved_hashes[pep423_name(req.name)] = { + 'version': clean_pkg_version(req.specifier), + 'hashes': pypi_hash + } - for result in r.resolve(): + for result in resolved_tree: name = pep423_name(result.name) - version = six.u(pep440_version(str(result.specifier))).replace('==', '') + version = clean_pkg_version(result.specifier) if hashes: try: @@ -66,6 +80,12 @@ def resolve_deps(deps, sources=None, verbose=False, hashes=False): collected_hashes.append(release['digests']['sha256']) collected_hashes = ['sha256:' + s for s in collected_hashes] + # Add pypi resolved hashes + if name in resolved_hashes and resolved_hashes[name]['version'] == version: + # Eliminate potential duplicate hashes + resolved = resolved_hashes[name]['hashes'] + resolved |= set(collected_hashes) + collected_hashes = list(resolved) results.append({'name': name, 'version': version, 'hashes': collected_hashes}) except ValueError: