Skip to content

Commit 86f34f8

Browse files
committed
fix lint
1 parent ea0f52b commit 86f34f8

File tree

2 files changed

+111
-4
lines changed

2 files changed

+111
-4
lines changed

pipenv/cli/options.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from pipenv.project import Project
55
from pipenv.utils import console, err
6+
from pipenv.utils.display import format_help
67
from pipenv.utils.internet import is_valid_url
78
from pipenv.vendor.click import (
89
BadArgumentUsage,
@@ -23,8 +24,6 @@ class PipenvGroup(DYMMixin, Group):
2324
"""Custom Group class provides formatted main help"""
2425

2526
def get_help_option(self, ctx):
26-
from pipenv.utils.display import format_help
27-
2827
"""Override for showing formatted main help via --help and -h options"""
2928
help_options = self.get_help_option_names(ctx)
3029
if not help_options or not self.add_help_option:

pipenv/utils/resolver.py

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import contextlib
2+
import hashlib
23
import json
34
import os
45
import subprocess
56
import sys
67
import tempfile
8+
import time
79
import warnings
810
from pathlib import Path
911
from typing import Any, Dict, List, Optional, Tuple, Union
@@ -24,7 +26,6 @@
2426
from pipenv.patched.pip._internal.req.req_install import InstallRequirement
2527
from pipenv.patched.pip._internal.utils.temp_dir import global_tempdir_manager
2628
from pipenv.patched.pip._vendor.packaging.utils import canonicalize_name
27-
from pipenv.project import Project
2829
from pipenv.utils import console, err
2930
from pipenv.utils.dependencies import determine_vcs_revision_hash, normalize_vcs_url
3031
from pipenv.utils.fileutils import create_tracked_tempdir
@@ -166,7 +167,7 @@ def check_if_package_req_skipped(
166167
def create(
167168
cls,
168169
deps: Dict[str, str],
169-
project: Project,
170+
project,
170171
index_lookup: Dict[str, str] = None,
171172
markers_lookup: Dict[str, str] = None,
172173
sources: List[str] = None,
@@ -707,6 +708,73 @@ def clean_results(self) -> List[Dict[str, Any]]:
707708
return list(results.values())
708709

709710

711+
# Global cache for resolution results to avoid repeated expensive subprocess calls
712+
_resolution_cache = {}
713+
_resolution_cache_timestamp = {}
714+
715+
716+
def _generate_resolution_cache_key(
717+
deps, project, pipfile_category, pre, clear, allow_global, pypi_mirror, extra_pip_args
718+
):
719+
"""Generate a cache key for resolution results."""
720+
# Get lockfile and pipfile modification times
721+
lockfile_mtime = "no-lock"
722+
if project.lockfile_location:
723+
lockfile_path = Path(project.lockfile_location)
724+
if lockfile_path.exists():
725+
lockfile_mtime = str(lockfile_path.stat().st_mtime)
726+
727+
pipfile_mtime = "no-pipfile"
728+
if project.pipfile_location:
729+
pipfile_path = Path(project.pipfile_location)
730+
if pipfile_path.exists():
731+
pipfile_mtime = str(pipfile_path.stat().st_mtime)
732+
733+
# Include environment variables that affect resolution
734+
env_factors = [
735+
os.environ.get("PIPENV_CACHE_VERSION", "1"),
736+
os.environ.get("PIPENV_PYPI_MIRROR", ""),
737+
os.environ.get("PIP_INDEX_URL", ""),
738+
str(pypi_mirror) if pypi_mirror else "",
739+
json.dumps(extra_pip_args, sort_keys=True) if extra_pip_args else "",
740+
]
741+
742+
# Create a deterministic representation of dependencies
743+
deps_str = json.dumps(deps, sort_keys=True) if isinstance(deps, dict) else str(deps)
744+
745+
key_components = [
746+
str(project.project_directory),
747+
lockfile_mtime,
748+
pipfile_mtime,
749+
deps_str,
750+
str(pipfile_category),
751+
str(pre),
752+
str(clear),
753+
str(allow_global),
754+
"|".join(env_factors),
755+
]
756+
757+
key_string = "|".join(key_components)
758+
return hashlib.md5(key_string.encode()).hexdigest()
759+
760+
761+
def _should_use_resolution_cache(cache_key, clear):
762+
"""Check if we should use cached resolution results."""
763+
if clear:
764+
return False
765+
766+
if cache_key not in _resolution_cache:
767+
return False
768+
769+
if cache_key not in _resolution_cache_timestamp:
770+
return False
771+
772+
# Cache is valid for 10 minutes
773+
current_time = time.time()
774+
cache_age = current_time - _resolution_cache_timestamp[cache_key]
775+
return cache_age < 600 # 10 minutes
776+
777+
710778
def _show_warning(message, category, filename, lineno, line):
711779
warnings.showwarning(
712780
message=message,
@@ -835,6 +903,31 @@ def venv_resolve_deps(
835903
lockfile = project.lockfile(categories=[pipfile_category])
836904
if old_lock_data is None:
837905
old_lock_data = lockfile.get(lockfile_category, {})
906+
907+
# Check cache before expensive resolution
908+
cache_key = _generate_resolution_cache_key(
909+
deps,
910+
project,
911+
pipfile_category,
912+
pre,
913+
clear,
914+
allow_global,
915+
pypi_mirror,
916+
extra_pip_args,
917+
)
918+
919+
if _should_use_resolution_cache(cache_key, clear):
920+
if project.s.is_verbose():
921+
err.print("[dim]Using cached resolution results...[/dim]")
922+
cached_results = _resolution_cache[cache_key]
923+
return prepare_lockfile(
924+
project,
925+
cached_results,
926+
pipfile,
927+
lockfile.get(lockfile_category, {}),
928+
old_lock_data,
929+
)
930+
838931
req_dir = create_tracked_tempdir(prefix="pipenv", suffix="requirements")
839932
results = []
840933
with temp_environ():
@@ -939,6 +1032,21 @@ def venv_resolve_deps(
9391032
)
9401033
err.print(f"Output: {c.stdout.strip()}")
9411034
err.print(f"Error: {c.stderr.strip()}")
1035+
1036+
# Cache the results for future use
1037+
if results:
1038+
_resolution_cache[cache_key] = results
1039+
_resolution_cache_timestamp[cache_key] = time.time()
1040+
1041+
# Clean old cache entries (keep only last 5 projects)
1042+
if len(_resolution_cache) > 5:
1043+
oldest_key = min(
1044+
_resolution_cache_timestamp.keys(),
1045+
key=lambda k: _resolution_cache_timestamp[k],
1046+
)
1047+
_resolution_cache.pop(oldest_key, None)
1048+
_resolution_cache_timestamp.pop(oldest_key, None)
1049+
9421050
if lockfile_category not in lockfile:
9431051
lockfile[lockfile_category] = {}
9441052
return prepare_lockfile(

0 commit comments

Comments
 (0)