From 189ae07c017efeb3a0b762b910e346baa9c09744 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Thu, 7 Sep 2017 11:36:24 -0400 Subject: [PATCH] =?UTF-8?q?grab=20hashes=20from=20pypi=E2=84=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kenneth Reitz --- pipenv/cli.py | 22 +++++++++++++--------- pipenv/utils.py | 26 ++++++++++++++++++++++++-- pipenv/vendor/pipfile/api.py | 2 +- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/pipenv/cli.py b/pipenv/cli.py index 1b7f57f4..0d533309 100644 --- a/pipenv/cli.py +++ b/pipenv/cli.py @@ -24,7 +24,7 @@ from requests.packages.urllib3.exceptions import InsecureRequestWarning from .project import Project from .utils import (convert_deps_from_pip, convert_deps_to_pip, is_required_version, - proper_case, pep423_name, split_vcs, resolve_deps) + proper_case, pep423_name, split_vcs, resolve_deps, shellquote) from .__version__ import __version__ from . import pep508checker, progress from .environments import (PIPENV_COLORBLIND, PIPENV_NOSPIN, PIPENV_SHELL_COMPAT, @@ -62,6 +62,9 @@ if PIPENV_NOSPIN: # Disable warnings for Python 2.6. requests.packages.urllib3.disable_warnings(InsecureRequestWarning) +# Requests session. +requests = requests.Session() + project = Project() @@ -460,10 +463,10 @@ def get_downloads_info(names_map, section): return info -def do_lock(no_hashes=True, verbose=False): +def do_lock(no_hashes=True, verbose=False, legacy=False): """Executes the freeze functionality.""" - if no_hashes: + if not legacy: # Alert the user of progress. click.echo(crayons.yellow('Locking {0} dependencies...'.format(crayons.red('[dev-packages]'))), err=True) @@ -478,7 +481,7 @@ def do_lock(no_hashes=True, verbose=False): # Resolve dev-package dependencies. deps = convert_deps_to_pip(project.dev_packages, r=False) - results = resolve_deps(deps, sources=project.sources, verbose=verbose) + results = resolve_deps(deps, sources=project.sources, verbose=verbose, hashes=(not no_hashes)) # Add develop dependencies to lockfile. for dep in results: @@ -489,16 +492,17 @@ def do_lock(no_hashes=True, verbose=False): # Resolve package dependencies. deps = convert_deps_to_pip(project.packages, r=False) - results = resolve_deps(deps, sources=project.sources) + results = resolve_deps(deps, sources=project.sources, hashes=(not no_hashes)) # Add default dependencies to lockfile. for dep in results: + print(dep) lockfile['default'].update({dep['name']: {'version': '=={0}'.format(dep['version'])}}) if not no_hashes: - lockfile['default'][dep['name']]['hash'] = dep['hash'] + lockfile['default'][dep['name']]['hashes'] = dep['hashes'] # Run the PEP 508 checker in the virtualenv, add it to the lockfile. - c = delegator.run('"{0}" {1}'.format(which('python'), pep508checker.__file__.rstrip('cdo'))) + c = delegator.run('"{0}" {1}'.format(which('python'), shellquote(pep508checker.__file__.rstrip('cdo')))) lockfile['_meta']['host-environment-markers'] = json.loads(c.out) # Write out the lockfile. @@ -552,7 +556,7 @@ def do_lock(no_hashes=True, verbose=False): lockfile['default'][dep['name']]['hash'] = dep['hash'] # Run the PEP 508 checker in the virtualenv, add it to the lockfile. - c = delegator.run('"{0}" {1}'.format(which('python'), pep508checker.__file__.rstrip('cdo'))) + c = delegator.run('"{0}" {1}'.format(which('python'), shellquote(pep508checker.__file__.rstrip('cdo')))) lockfile['_meta']['host-environment-markers'] = json.loads(c.out) # Write out lockfile. @@ -1171,7 +1175,7 @@ def check(three=None, python=False): click.echo(crayons.yellow('Checking PEP 508 requirements...')) # Run the PEP 508 checker in the virtualenv. - c = delegator.run('"{0}" {1}'.format(which('python'), pep508checker.__file__.rstrip('cdo'))) + c = delegator.run('"{0}" {1}'.format(which('python'), shellquote(pep508checker.__file__.rstrip('cdo')))) results = json.loads(c.out) # Load the pipfile. diff --git a/pipenv/utils.py b/pipenv/utils.py index c016e761..6b4329b4 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -22,7 +22,11 @@ class PipCommand(pip.basecommand.Command): name = 'PipCommand' -def resolve_deps(deps, sources=None, verbose=False): +def shellquote(s): + return "'" + s.replace("'", "'\\''") + "'" + + +def resolve_deps(deps, sources=None, verbose=False, hashes=False): constraints = [] @@ -51,7 +55,25 @@ def resolve_deps(deps, sources=None, verbose=False): results = [] for result in r.resolve(): - results.append({'name': pep423_name(result.name), 'version': six.u(str(result.specifier)).replace('==', '')}) + name = pep423_name(result.name) + version = six.u(str(result.specifier)).replace('==', '') + + from json.decoder import JSONDecodeError + + if hashes: + try: + collected_hashes = [] + r = requests.get('https://pypi.org/pypi/{0}/json'.format(name)) + for release in r.json()['releases'][version]: + collected_hashes.append(release['digests']['sha256']) + + collected_hashes = ['sha256:' + s for s in collected_hashes] + + results.append({'name': name, 'version': version, 'hashes': collected_hashes}) + except JSONDecodeError: + results.append({'name': name, 'version': version}) + else: + results.append({'name': name, 'version': version}) return results diff --git a/pipenv/vendor/pipfile/api.py b/pipenv/vendor/pipfile/api.py index 1c4874b3..f149d63d 100644 --- a/pipenv/vendor/pipfile/api.py +++ b/pipenv/vendor/pipfile/api.py @@ -142,7 +142,7 @@ class Pipfile(object): """Returns a JSON representation of the Pipfile.""" data = self.data data['_meta']['hash'] = {"sha256": self.hash} - data['_meta']['pipfile-spec'] = 1 + data['_meta']['pipfile-spec'] = 2 # return _json.dumps(data) return json.dumps(data, indent=4, separators=(',', ': '))