Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add back --skip-lock flag using the new utilities. #5847

Merged
merged 6 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
2023.8.20 (2023-08-20)
======================
Pipenv 2023.8.20 (2023-08-20)
=============================


Bug Fixes
---------
Expand All @@ -12,9 +9,6 @@ Bug Fixes

2023.8.19 (2023-08-19)
======================
Pipenv 2023.8.19 (2023-08-19)
=============================


Features & Improvements
-----------------------
Expand Down
1 change: 1 addition & 0 deletions pipenv/cli/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ def install(state, **kwargs):
site_packages=state.site_packages,
extra_pip_args=state.installstate.extra_pip_args,
categories=state.installstate.categories,
skip_lock=state.installstate.skip_lock,
)


Expand Down
15 changes: 8 additions & 7 deletions pipenv/cli/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def __init__(self):
self.editables = []
self.extra_pip_args = []
self.categories = []
self.skip_lock = False


class LockOptions:
Expand Down Expand Up @@ -484,32 +485,32 @@ def validate_pypi_mirror(ctx, param, value):
return value


# OLD REMOVED COMMANDS THAT WE STILL DISPLAY HELP TEXT FOR #
def skip_lock_option(f):
def callback(ctx, param, value):
if value:
err.print(
"The flag --skip-lock has been functionally removed. "
"Without running the lock resolver it is not possible to manage multiple package indexes. "
"Additionally it bypassed the build consistency guarantees provided by maintaining a lock file.",
"The flag --skip-lock has been reintroduced (but is not recommended). "
"Without the lock resolver it is difficult to manage multiple package indexes, and hash checking is not provided. "
"However it can help manage installs with current deficiencies in locking across platforms.",
style="yellow bold",
)
raise ValueError("The flag --skip-lock flag has been removed.")
state = ctx.ensure_object(State)
state.installstate.skip_lock = value
return value

return option(
"--skip-lock",
is_flag=True,
default=False,
expose_value=False,
expose_value=True,
envvar="PIPENV_SKIP_LOCK",
callback=callback,
type=click_types.BOOL,
show_envvar=True,
hidden=True, # This hides the option from the help text.
)(f)


# OLD REMOVED COMMANDS THAT WE STILL DISPLAY HELP TEXT FOR WHEN USED #
def keep_outdated_option(f):
def callback(ctx, param, value):
state = ctx.ensure_object(State)
Expand Down
71 changes: 53 additions & 18 deletions pipenv/routines/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from pipenv.utils.dependencies import (
expansive_install_req_from_line,
get_lockfile_section_using_pipfile_category,
install_req_from_pipfile,
)
from pipenv.utils.indexes import get_source_list
from pipenv.utils.internet import download_file, is_valid_url
Expand Down Expand Up @@ -42,6 +43,7 @@ def do_install(
site_packages=None,
extra_pip_args=None,
categories=None,
skip_lock=False,
):
requirements_directory = fileutils.create_tracked_tempdir(
suffix="-requirements", prefix="pipenv-"
Expand Down Expand Up @@ -70,7 +72,7 @@ def do_install(
if not project.pipfile_exists and not (package_args or dev):
if not (ignore_pipfile or deploy):
raise exceptions.PipfileNotFound(project.path_to("Pipfile"))
elif ignore_pipfile and not project.lockfile_exists:
elif ((skip_lock and deploy) or ignore_pipfile) and not project.lockfile_exists:
raise exceptions.LockfileNotFound(project.path_to("Pipfile.lock"))
# Load the --pre settings from the Pipfile.
if not pre:
Expand Down Expand Up @@ -171,6 +173,7 @@ def do_install(
pypi_mirror=pypi_mirror,
extra_pip_args=extra_pip_args,
categories=categories,
skip_lock=skip_lock,
)

# This is for if the user passed in dependencies, then we want to make sure we
Expand All @@ -188,6 +191,7 @@ def do_install(
pypi_mirror=pypi_mirror,
extra_pip_args=extra_pip_args,
categories=categories,
skip_lock=skip_lock,
)

for pkg_line in pkg_list:
Expand Down Expand Up @@ -284,6 +288,7 @@ def do_install(
pypi_mirror=pypi_mirror,
extra_pip_args=extra_pip_args,
categories=categories,
skip_lock=skip_lock,
)
except Exception as e:
# If we fail to install, remove the package from the Pipfile.
Expand Down Expand Up @@ -358,6 +363,7 @@ def do_install_dependencies(
pypi_mirror=None,
extra_pip_args=None,
categories=None,
skip_lock=False,
):
"""
Executes the installation functionality.
Expand All @@ -373,17 +379,39 @@ def do_install_dependencies(
categories = ["packages"]

for category in categories:
lockfile = project.get_or_create_lockfile(categories=categories)
if not bare:
console.print(
f"Installing dependencies from Pipfile.lock "
f"({lockfile['_meta'].get('hash', {}).get('sha256')[-6:]})...",
style="bold",
)
# Load the lockfile if it exists, or if dev_only is being used.
lockfile = None
pipfile = None
if skip_lock:
ignore_hashes = True
if not bare:
console.print("Installing dependencies from Pipfile...", style="bold")
pipfile = project.get_pipfile_section(category)
else:
lockfile = project.get_or_create_lockfile(categories=categories)
if not bare:
console.print(
f"Installing dependencies from Pipfile.lock "
f"({lockfile['_meta'].get('hash', {}).get('sha256')[-6:]})...",
style="bold",
)
dev = dev or dev_only
deps_list = list(
lockfile.get_requirements(dev=dev, only=dev_only, categories=[category])
)
if skip_lock:
deps_list = []
for req_name, pipfile_entry in pipfile.items():
install_req, markers, req_line = install_req_from_pipfile(
req_name, pipfile_entry
)
deps_list.append(
(
install_req,
req_line,
)
)
else:
deps_list = list(
lockfile.get_requirements(dev=dev, only=dev_only, categories=[category])
)
editable_or_vcs_deps = [
(dep, pip_line) for dep, pip_line in deps_list if (dep.link and dep.editable)
]
Expand All @@ -394,15 +422,18 @@ def do_install_dependencies(
]

install_kwargs = {
"no_deps": True,
"no_deps": not skip_lock,
"ignore_hashes": ignore_hashes,
"allow_global": allow_global,
"pypi_mirror": pypi_mirror,
"sequential_deps": editable_or_vcs_deps,
"extra_pip_args": extra_pip_args,
}
lockfile_category = get_lockfile_section_using_pipfile_category(category)
lockfile_section = lockfile[lockfile_category]
if skip_lock:
lockfile_section = pipfile
else:
lockfile_category = get_lockfile_section_using_pipfile_category(category)
lockfile_section = lockfile[lockfile_category]
batch_install(
project,
normal_deps,
Expand Down Expand Up @@ -499,8 +530,10 @@ def batch_install(
deps_by_index = defaultdict(list)
for dependency, pip_line in deps_to_install:
index = project.sources_default["name"]
if dependency.name and lockfile_section[dependency.name].get("index"):
index = lockfile_section[dependency.name]["index"]
if dependency.name and dependency.name in lockfile_section:
entry = lockfile_section[dependency.name]
if isinstance(entry, dict) and "index" in entry:
index = entry["index"]
deps_by_index[index].append(pip_line)
# Treat each index as its own pip install phase
for index_name, dependencies in deps_by_index.items():
Expand Down Expand Up @@ -560,6 +593,7 @@ def do_init(
pypi_mirror=None,
extra_pip_args=None,
categories=None,
skip_lock=False,
):
"""Executes the init functionality."""
python = None
Expand All @@ -585,7 +619,7 @@ def do_init(
suffix="-requirements", prefix="pipenv-"
)
# Write out the lockfile if it doesn't exist, but not if the Pipfile is being ignored
if project.lockfile_exists and not ignore_pipfile:
if (project.lockfile_exists and not ignore_pipfile) and not skip_lock:
old_hash = project.get_lockfile_hash()
new_hash = project.calculate_pipfile_hash()
if new_hash != old_hash:
Expand Down Expand Up @@ -620,7 +654,7 @@ def do_init(
categories=categories,
)
# Write out the lockfile if it doesn't exist.
if not project.lockfile_exists:
if not project.lockfile_exists and not skip_lock:
# Unless we're in a virtualenv not managed by pipenv, abort if we're
# using the system's python.
if (system or allow_global) and not (project.s.PIPENV_VIRTUALENV):
Expand Down Expand Up @@ -652,6 +686,7 @@ def do_init(
pypi_mirror=pypi_mirror,
extra_pip_args=extra_pip_args,
categories=categories,
skip_lock=skip_lock,
)

# Hint the user what to do to activate the virtualenv.
Expand Down
9 changes: 4 additions & 5 deletions pipenv/utils/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def unearth_hashes_for_dep(project, dep):
break

# 1 Try to get hashes directly form index
install_req, markers = install_req_from_pipfile(dep["name"], dep)
install_req, markers, _ = install_req_from_pipfile(dep["name"], dep)
if not install_req or not install_req.req:
return []
if "https://pypi.org/simple/" in index_url:
Expand Down Expand Up @@ -981,7 +981,7 @@ def install_req_from_pipfile(name, pipfile):
vcs_url_parts = vcs_url.rsplit("@", 1)
vcs_url = vcs_url_parts[0]
fallback_ref = vcs_url_parts[1]
req_str = f"{vcs_url}{_pipfile.get('ref', fallback_ref)}{extras_str}"
req_str = f"{vcs_url}@{_pipfile.get('ref', fallback_ref)}{extras_str}"
if not req_str.startswith(f"{vcs}+"):
req_str = f"{vcs}+{req_str}"
if f"{vcs}+file://" in req_str:
Expand Down Expand Up @@ -1011,12 +1011,11 @@ def install_req_from_pipfile(name, pipfile):
expand_env=True,
)
markers = PipenvMarkers.from_pipfile(name, _pipfile)
return install_req, markers
return install_req, markers, req_str


def from_pipfile(name, pipfile):
install_req, markers = install_req_from_pipfile(name, pipfile)

install_req, markers, req_str = install_req_from_pipfile(name, pipfile)
if markers:
markers = str(markers)
install_req.markers = Marker(markers)
Expand Down
19 changes: 19 additions & 0 deletions tests/integration/test_install_twists.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,22 @@ def test_install_remote_wheel_file_with_extras(pipenv_instance_pypi):
assert "pre-commit" in p.lockfile["default"]
assert "uvicorn" in p.lockfile["default"]

@pytest.mark.install
@pytest.mark.skip_lock
@pytest.mark.needs_internet
def test_install_skip_lock(pipenv_instance_private_pypi):
with pipenv_instance_private_pypi() as p:
with open(p.pipfile_path, 'w') as f:
contents = """
[[source]]
url = "{}"
verify_ssl = true
name = "pypi"
[packages]
six = {}
""".format(p.index_url, '{version = "*", index = "pypi"}').strip()
f.write(contents)
c = p.pipenv('install --skip-lock')
assert c.returncode == 0
c = p.pipenv('run python -c "import six"')
assert c.returncode == 0
13 changes: 13 additions & 0 deletions tests/integration/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,16 @@ def test_run_in_virtualenv(pipenv_instance_pypi):
c = p.pipenv("clean --dry-run")
assert c.returncode == 0
assert "click" in c.stdout

@pytest.mark.project
@pytest.mark.sources
def test_no_sources_in_pipfile(pipenv_instance_pypi):
with pipenv_instance_pypi() as p:
with open(p.pipfile_path, 'w') as f:
contents = """
[packages]
pytest = "*"
""".strip()
f.write(contents)
c = p.pipenv('install --skip-lock')
assert c.returncode == 0