From 2d4142e5649844f3d15fc5013a3be9718485d24f Mon Sep 17 00:00:00 2001 From: Cansin Yildiz Date: Tue, 3 Oct 2023 22:38:42 +0300 Subject: [PATCH] feat(install): add 'exact' option addresses https://github.com/pypa/pipenv/issues/5531 --- pipenv/cli/command.py | 1 + pipenv/cli/options.py | 20 ++++++++++++++++++++ pipenv/project.py | 12 +++++++++++- pipenv/routines/install.py | 9 +++++++-- 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/pipenv/cli/command.py b/pipenv/cli/command.py index fb4ee0589d..6c0ffb65c2 100644 --- a/pipenv/cli/command.py +++ b/pipenv/cli/command.py @@ -212,6 +212,7 @@ def install(state, **kwargs): python=state.python, pypi_mirror=state.pypi_mirror, system=state.system, + exact=state.installstate.exact, ignore_pipfile=state.installstate.ignore_pipfile, requirementstxt=state.installstate.requirementstxt, pre=state.installstate.pre, diff --git a/pipenv/cli/options.py b/pipenv/cli/options.py index 701cf9ed83..ccd9db783d 100644 --- a/pipenv/cli/options.py +++ b/pipenv/cli/options.py @@ -77,6 +77,7 @@ class InstallState: def __init__(self): self.dev = False self.pre = False + self.exact = False self.ignore_pipfile = False self.code = False self.requirementstxt = None @@ -130,6 +131,24 @@ def callback(ctx, param, value): )(f) +def exact_option(f): + def callback(ctx, param, value): + state = ctx.ensure_object(State) + state.installstate.exact = value + return value + + return option( + "--exact", + is_flag=True, + default=False, + expose_value=False, + help="Add exact package version to Pipfile when installing, instead of *.", + callback=callback, + type=click_types.BOOL, + show_envvar=True, + )(f) + + def ignore_pipfile_option(f): def callback(ctx, param, value): state = ctx.ensure_object(State) @@ -607,6 +626,7 @@ def install_options(f): f = sync_options(f) f = index_option(f) f = requirementstxt_option(f) + f = exact_option(f) f = ignore_pipfile_option(f) f = editable_option(f) f = package_arg(f) diff --git a/pipenv/project.py b/pipenv/project.py index b96487c108..0329c542a2 100644 --- a/pipenv/project.py +++ b/pipenv/project.py @@ -1197,13 +1197,23 @@ def generate_package_pipfile_entry(self, package, pip_line, category=None): else: return name, normalized_name, entry - def add_package_to_pipfile(self, package, pip_line, dev=False, category=None): + def add_package_to_pipfile( + self, package, pip_line, dev=False, category=None, exact=False + ): category = category if category else "dev-packages" if dev else "packages" name, normalized_name, entry = self.generate_package_pipfile_entry( package, pip_line, category=category ) + if exact: + from pipenv.utils.resolver import resolve_deps + + resolved_packages, resolver = resolve_deps( + {normalized_name: package.name}, None, self, category=category + ) + entry = str(resolver.install_reqs[normalized_name].specifier) + return self.add_pipfile_entry_to_pipfile( name, normalized_name, entry, category=category ) diff --git a/pipenv/routines/install.py b/pipenv/routines/install.py index 2f135b4354..c38efe9698 100644 --- a/pipenv/routines/install.py +++ b/pipenv/routines/install.py @@ -36,6 +36,7 @@ def do_install( python=False, pypi_mirror=None, system=False, + exact=False, ignore_pipfile=False, requirementstxt=False, pre=False, @@ -243,7 +244,11 @@ def do_install( if categories: for category in categories: added, cat, normalized_name = project.add_package_to_pipfile( - pkg_requirement, pkg_line, dev, category + pkg_requirement, + pkg_line, + dev, + category=category, + exact=exact, ) if added: new_packages.append((normalized_name, cat)) @@ -253,7 +258,7 @@ def do_install( ) else: added, cat, normalized_name = project.add_package_to_pipfile( - pkg_requirement, pkg_line, dev + pkg_requirement, pkg_line, dev, exact=exact ) if added: new_packages.append((normalized_name, cat))