Skip to content

Commit c8f4465

Browse files
authored
Add back --skip-lock flag using the new utilities. (#5847)
* Add back --skip-lock flag using the new utilities.
1 parent d43a7ae commit c8f4465

File tree

7 files changed

+98
-36
lines changed

7 files changed

+98
-36
lines changed

CHANGELOG.rst

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ Vendored Libraries
1818

1919
2023.8.20 (2023-08-20)
2020
======================
21-
Pipenv 2023.8.20 (2023-08-20)
22-
=============================
23-
2421

2522
Bug Fixes
2623
---------
@@ -30,9 +27,6 @@ Bug Fixes
3027

3128
2023.8.19 (2023-08-19)
3229
======================
33-
Pipenv 2023.8.19 (2023-08-19)
34-
=============================
35-
3630

3731
Features & Improvements
3832
-----------------------

pipenv/cli/command.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ def install(state, **kwargs):
222222
site_packages=state.site_packages,
223223
extra_pip_args=state.installstate.extra_pip_args,
224224
categories=state.installstate.categories,
225+
skip_lock=state.installstate.skip_lock,
225226
)
226227

227228

pipenv/cli/options.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ def __init__(self):
8585
self.editables = []
8686
self.extra_pip_args = []
8787
self.categories = []
88+
self.skip_lock = False
8889

8990

9091
class LockOptions:
@@ -484,32 +485,32 @@ def validate_pypi_mirror(ctx, param, value):
484485
return value
485486

486487

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

500501
return option(
501502
"--skip-lock",
502503
is_flag=True,
503504
default=False,
504-
expose_value=False,
505+
expose_value=True,
505506
envvar="PIPENV_SKIP_LOCK",
506507
callback=callback,
507508
type=click_types.BOOL,
508509
show_envvar=True,
509-
hidden=True, # This hides the option from the help text.
510510
)(f)
511511

512512

513+
# OLD REMOVED COMMANDS THAT WE STILL DISPLAY HELP TEXT FOR WHEN USED #
513514
def keep_outdated_option(f):
514515
def callback(ctx, param, value):
515516
state = ctx.ensure_object(State)

pipenv/routines/install.py

Lines changed: 53 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from pipenv.utils.dependencies import (
1313
expansive_install_req_from_line,
1414
get_lockfile_section_using_pipfile_category,
15+
install_req_from_pipfile,
1516
)
1617
from pipenv.utils.indexes import get_source_list
1718
from pipenv.utils.internet import download_file, is_valid_url
@@ -42,6 +43,7 @@ def do_install(
4243
site_packages=None,
4344
extra_pip_args=None,
4445
categories=None,
46+
skip_lock=False,
4547
):
4648
requirements_directory = fileutils.create_tracked_tempdir(
4749
suffix="-requirements", prefix="pipenv-"
@@ -70,7 +72,7 @@ def do_install(
7072
if not project.pipfile_exists and not (package_args or dev):
7173
if not (ignore_pipfile or deploy):
7274
raise exceptions.PipfileNotFound(project.path_to("Pipfile"))
73-
elif ignore_pipfile and not project.lockfile_exists:
75+
elif ((skip_lock and deploy) or ignore_pipfile) and not project.lockfile_exists:
7476
raise exceptions.LockfileNotFound(project.path_to("Pipfile.lock"))
7577
# Load the --pre settings from the Pipfile.
7678
if not pre:
@@ -171,6 +173,7 @@ def do_install(
171173
pypi_mirror=pypi_mirror,
172174
extra_pip_args=extra_pip_args,
173175
categories=categories,
176+
skip_lock=skip_lock,
174177
)
175178

176179
# This is for if the user passed in dependencies, then we want to make sure we
@@ -188,6 +191,7 @@ def do_install(
188191
pypi_mirror=pypi_mirror,
189192
extra_pip_args=extra_pip_args,
190193
categories=categories,
194+
skip_lock=skip_lock,
191195
)
192196

193197
for pkg_line in pkg_list:
@@ -284,6 +288,7 @@ def do_install(
284288
pypi_mirror=pypi_mirror,
285289
extra_pip_args=extra_pip_args,
286290
categories=categories,
291+
skip_lock=skip_lock,
287292
)
288293
except Exception as e:
289294
# If we fail to install, remove the package from the Pipfile.
@@ -358,6 +363,7 @@ def do_install_dependencies(
358363
pypi_mirror=None,
359364
extra_pip_args=None,
360365
categories=None,
366+
skip_lock=False,
361367
):
362368
"""
363369
Executes the installation functionality.
@@ -373,17 +379,39 @@ def do_install_dependencies(
373379
categories = ["packages"]
374380

375381
for category in categories:
376-
lockfile = project.get_or_create_lockfile(categories=categories)
377-
if not bare:
378-
console.print(
379-
f"Installing dependencies from Pipfile.lock "
380-
f"({lockfile['_meta'].get('hash', {}).get('sha256')[-6:]})...",
381-
style="bold",
382-
)
382+
# Load the lockfile if it exists, or if dev_only is being used.
383+
lockfile = None
384+
pipfile = None
385+
if skip_lock:
386+
ignore_hashes = True
387+
if not bare:
388+
console.print("Installing dependencies from Pipfile...", style="bold")
389+
pipfile = project.get_pipfile_section(category)
390+
else:
391+
lockfile = project.get_or_create_lockfile(categories=categories)
392+
if not bare:
393+
console.print(
394+
f"Installing dependencies from Pipfile.lock "
395+
f"({lockfile['_meta'].get('hash', {}).get('sha256')[-6:]})...",
396+
style="bold",
397+
)
383398
dev = dev or dev_only
384-
deps_list = list(
385-
lockfile.get_requirements(dev=dev, only=dev_only, categories=[category])
386-
)
399+
if skip_lock:
400+
deps_list = []
401+
for req_name, pipfile_entry in pipfile.items():
402+
install_req, markers, req_line = install_req_from_pipfile(
403+
req_name, pipfile_entry
404+
)
405+
deps_list.append(
406+
(
407+
install_req,
408+
req_line,
409+
)
410+
)
411+
else:
412+
deps_list = list(
413+
lockfile.get_requirements(dev=dev, only=dev_only, categories=[category])
414+
)
387415
editable_or_vcs_deps = [
388416
(dep, pip_line) for dep, pip_line in deps_list if (dep.link and dep.editable)
389417
]
@@ -394,15 +422,18 @@ def do_install_dependencies(
394422
]
395423

396424
install_kwargs = {
397-
"no_deps": True,
425+
"no_deps": not skip_lock,
398426
"ignore_hashes": ignore_hashes,
399427
"allow_global": allow_global,
400428
"pypi_mirror": pypi_mirror,
401429
"sequential_deps": editable_or_vcs_deps,
402430
"extra_pip_args": extra_pip_args,
403431
}
404-
lockfile_category = get_lockfile_section_using_pipfile_category(category)
405-
lockfile_section = lockfile[lockfile_category]
432+
if skip_lock:
433+
lockfile_section = pipfile
434+
else:
435+
lockfile_category = get_lockfile_section_using_pipfile_category(category)
436+
lockfile_section = lockfile[lockfile_category]
406437
batch_install(
407438
project,
408439
normal_deps,
@@ -499,8 +530,10 @@ def batch_install(
499530
deps_by_index = defaultdict(list)
500531
for dependency, pip_line in deps_to_install:
501532
index = project.sources_default["name"]
502-
if dependency.name and lockfile_section[dependency.name].get("index"):
503-
index = lockfile_section[dependency.name]["index"]
533+
if dependency.name and dependency.name in lockfile_section:
534+
entry = lockfile_section[dependency.name]
535+
if isinstance(entry, dict) and "index" in entry:
536+
index = entry["index"]
504537
deps_by_index[index].append(pip_line)
505538
# Treat each index as its own pip install phase
506539
for index_name, dependencies in deps_by_index.items():
@@ -560,6 +593,7 @@ def do_init(
560593
pypi_mirror=None,
561594
extra_pip_args=None,
562595
categories=None,
596+
skip_lock=False,
563597
):
564598
"""Executes the init functionality."""
565599
python = None
@@ -585,7 +619,7 @@ def do_init(
585619
suffix="-requirements", prefix="pipenv-"
586620
)
587621
# Write out the lockfile if it doesn't exist, but not if the Pipfile is being ignored
588-
if project.lockfile_exists and not ignore_pipfile:
622+
if (project.lockfile_exists and not ignore_pipfile) and not skip_lock:
589623
old_hash = project.get_lockfile_hash()
590624
new_hash = project.calculate_pipfile_hash()
591625
if new_hash != old_hash:
@@ -620,7 +654,7 @@ def do_init(
620654
categories=categories,
621655
)
622656
# Write out the lockfile if it doesn't exist.
623-
if not project.lockfile_exists:
657+
if not project.lockfile_exists and not skip_lock:
624658
# Unless we're in a virtualenv not managed by pipenv, abort if we're
625659
# using the system's python.
626660
if (system or allow_global) and not (project.s.PIPENV_VIRTUALENV):
@@ -652,6 +686,7 @@ def do_init(
652686
pypi_mirror=pypi_mirror,
653687
extra_pip_args=extra_pip_args,
654688
categories=categories,
689+
skip_lock=skip_lock,
655690
)
656691

657692
# Hint the user what to do to activate the virtualenv.

pipenv/utils/dependencies.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ def unearth_hashes_for_dep(project, dep):
185185
break
186186

187187
# 1 Try to get hashes directly form index
188-
install_req, markers = install_req_from_pipfile(dep["name"], dep)
188+
install_req, markers, _ = install_req_from_pipfile(dep["name"], dep)
189189
if not install_req or not install_req.req:
190190
return []
191191
if "https://pypi.org/simple/" in index_url:
@@ -981,7 +981,7 @@ def install_req_from_pipfile(name, pipfile):
981981
vcs_url_parts = vcs_url.rsplit("@", 1)
982982
vcs_url = vcs_url_parts[0]
983983
fallback_ref = vcs_url_parts[1]
984-
req_str = f"{vcs_url}{_pipfile.get('ref', fallback_ref)}{extras_str}"
984+
req_str = f"{vcs_url}@{_pipfile.get('ref', fallback_ref)}{extras_str}"
985985
if not req_str.startswith(f"{vcs}+"):
986986
req_str = f"{vcs}+{req_str}"
987987
if f"{vcs}+file://" in req_str:
@@ -1011,12 +1011,11 @@ def install_req_from_pipfile(name, pipfile):
10111011
expand_env=True,
10121012
)
10131013
markers = PipenvMarkers.from_pipfile(name, _pipfile)
1014-
return install_req, markers
1014+
return install_req, markers, req_str
10151015

10161016

10171017
def from_pipfile(name, pipfile):
1018-
install_req, markers = install_req_from_pipfile(name, pipfile)
1019-
1018+
install_req, markers, req_str = install_req_from_pipfile(name, pipfile)
10201019
if markers:
10211020
markers = str(markers)
10221021
install_req.markers = Marker(markers)

tests/integration/test_install_twists.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,3 +286,22 @@ def test_install_remote_wheel_file_with_extras(pipenv_instance_pypi):
286286
assert "pre-commit" in p.lockfile["default"]
287287
assert "uvicorn" in p.lockfile["default"]
288288

289+
@pytest.mark.install
290+
@pytest.mark.skip_lock
291+
@pytest.mark.needs_internet
292+
def test_install_skip_lock(pipenv_instance_private_pypi):
293+
with pipenv_instance_private_pypi() as p:
294+
with open(p.pipfile_path, 'w') as f:
295+
contents = """
296+
[[source]]
297+
url = "{}"
298+
verify_ssl = true
299+
name = "pypi"
300+
[packages]
301+
six = {}
302+
""".format(p.index_url, '{version = "*", index = "pypi"}').strip()
303+
f.write(contents)
304+
c = p.pipenv('install --skip-lock')
305+
assert c.returncode == 0
306+
c = p.pipenv('run python -c "import six"')
307+
assert c.returncode == 0

tests/integration/test_project.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,16 @@ def test_run_in_virtualenv(pipenv_instance_pypi):
162162
c = p.pipenv("clean --dry-run")
163163
assert c.returncode == 0
164164
assert "click" in c.stdout
165+
166+
@pytest.mark.project
167+
@pytest.mark.sources
168+
def test_no_sources_in_pipfile(pipenv_instance_pypi):
169+
with pipenv_instance_pypi() as p:
170+
with open(p.pipfile_path, 'w') as f:
171+
contents = """
172+
[packages]
173+
pytest = "*"
174+
""".strip()
175+
f.write(contents)
176+
c = p.pipenv('install --skip-lock')
177+
assert c.returncode == 0

0 commit comments

Comments
 (0)