Skip to content

Commit

Permalink
Merge pull request #6276 from pypa/issue-6267
Browse files Browse the repository at this point in the history
install should not do full lock resolution, as that is the purpose of pipenv lock
  • Loading branch information
matteius authored Oct 22, 2024
2 parents 792ac5c + 526ed94 commit b872d0b
Show file tree
Hide file tree
Showing 34 changed files with 870 additions and 436 deletions.
4 changes: 0 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,6 @@ For most users, we recommend installing Pipenv using `pip`:

pip install --user pipenv

Or, if you\'re using Fedora:

sudo dnf install pipenv

Or, if you\'re using FreeBSD:

pkg install py39-pipenv
Expand Down
15 changes: 9 additions & 6 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,18 @@
#
import os

import pipenv.vendor.click

# Hackery to get the CLI docs to generate
from pipenv.vendor import click

# Path hackery to get current version number.
here = os.path.abspath(os.path.dirname(__file__))

about = {}
with open(os.path.join(here, "..", "pipenv", "__version__.py")) as f:
exec(f.read(), about)

# Hackery to get the CLI docs to generate
import click

import pipenv.vendor.click

click.Command = pipenv.vendor.click.Command
click.Group = pipenv.vendor.click.Group
click.BaseCommand = pipenv.vendor.click.BaseCommand
Expand Down Expand Up @@ -80,7 +80,10 @@

# General information about the project.
project = "pipenv"
copyright = '2020. A project founded by Kenneth Reitz and maintained by <a href="https://www.pypa.io/en/latest/">Python Packaging Authority (PyPA).</a>'
copyright = (
"2020. A project founded by Kenneth Reitz and maintained by "
'<a href="https://www.pypa.io/en/latest/">Python Packaging Authority (PyPA).</a>'
)
author = "Python Packaging Authority"

# The version info for the project you're documenting, acts as replacement for
Expand Down
13 changes: 13 additions & 0 deletions news/6276.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Features & Bug Fixes
-------------------
- Refactored and simplified install routines, improving maintainability and reliability (#6276)
- Split install logic into smaller, focused functions.
- Eliminated Pipfile caching for now to prevent bugs and reduce complexity.
- Fixed edge cases with package category selection.
- Improved handling of VCS dependencies during updates, fixing when ref is a revision and not a branch.

- Enhanced VCS URL handling with better environment variable support (#6276)
- More reliable expansion of environment variables in Git URLs.
- Better handling of authentication components in VCS URLs.
- Improved error messaging for missing environment variables.
- Fixed issue where Git reference could be dropped during relock.
58 changes: 21 additions & 37 deletions pipenv/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,6 @@ def preferred_newlines(f):
return DEFAULT_NEWLINES


# (path, file contents) => TOMLFile
# keeps track of pipfiles that we've seen so we do not need to re-parse 'em
_pipfile_cache = {}


class SourceNotFound(KeyError):
pass

Expand Down Expand Up @@ -670,16 +665,9 @@ def requirements_location(self) -> str | None:

@property
def parsed_pipfile(self) -> tomlkit.toml_document.TOMLDocument | TPipfile:
"""Parse Pipfile into a TOMLFile and cache it
(call clear_pipfile_cache() afterwards if mutating)"""
"""Parse Pipfile into a TOMLFile"""
contents = self.read_pipfile()
# use full contents to get around str/bytes 2/3 issues
cache_key = (self.pipfile_location, contents)
if cache_key not in _pipfile_cache:
parsed = self._parse_pipfile(contents)
_pipfile_cache[cache_key] = parsed
return _pipfile_cache[cache_key]
return self._parse_pipfile(contents)

def read_pipfile(self) -> str:
# Open the pipfile, read it into memory.
Expand All @@ -691,10 +679,6 @@ def read_pipfile(self) -> str:

return contents

def clear_pipfile_cache(self) -> None:
"""Clear pipfile cache (e.g., so we can mutate parsed pipfile)"""
_pipfile_cache.clear()

def _parse_pipfile(
self, contents: str
) -> tomlkit.toml_document.TOMLDocument | TPipfile:
Expand Down Expand Up @@ -991,8 +975,6 @@ def write_toml(self, data, path=None):
formatted_data = cleanup_toml(formatted_data)
with open(path, "w", newline=newlines) as f:
f.write(formatted_data)
# pipfile is mutated!
self.clear_pipfile_cache()

def write_lockfile(self, content):
"""Write out the lockfile."""
Expand Down Expand Up @@ -1088,7 +1070,6 @@ def find_source(sources, name=None, url=None):

sources = (self.sources, self.pipfile_sources())
if refresh:
self.clear_pipfile_cache()
sources = reversed(sources)
found = next(
iter(find_source(source, name=name, url=url) for source in sources), None
Expand All @@ -1099,15 +1080,16 @@ def find_source(sources, name=None, url=None):
return found

def get_package_name_in_pipfile(self, package_name, category):
"""Get the equivalent package name in pipfile"""
section = self.parsed_pipfile.get(category)
if section is None:
section = {}
package_name = pep423_name(package_name)
section = self.parsed_pipfile.get(category, {})
normalized_name = pep423_name(package_name)
for name in section:
if pep423_name(name) == package_name:
if pep423_name(name) == normalized_name:
return name
return None
return package_name # Return original name if not found

def get_pipfile_entry(self, package_name, category):
name = self.get_package_name_in_pipfile(package_name, category)
return self.parsed_pipfile.get(category, {}).get(name)

def _sort_category(self, category) -> Table:
# copy table or create table from dict-like object
Expand Down Expand Up @@ -1244,26 +1226,28 @@ def add_pipfile_entry_to_pipfile(self, name, normalized_name, entry, category=No
newly_added = False

# Read and append Pipfile.
p = self.parsed_pipfile
parsed_pipfile = self.parsed_pipfile

# Set empty group if it doesn't exist yet.
if category not in p:
p[category] = {}
if category not in parsed_pipfile:
parsed_pipfile[category] = {}

if name and name != normalized_name:
self.remove_package_from_pipfile(name, category=category)
section = parsed_pipfile.get(category, {})
for entry_name in section.copy().keys():
if entry_name.lower() == normalized_name.lower():
del parsed_pipfile[category][entry_name]

# Add the package to the group.
if normalized_name not in p[category]:
if normalized_name not in parsed_pipfile[category]:
newly_added = True

p[category][normalized_name] = entry
parsed_pipfile[category][normalized_name] = entry

if self.settings.get("sort_pipfile"):
p[category] = self._sort_category(p[category])
parsed_pipfile[category] = self._sort_category(parsed_pipfile[category])

# Write Pipfile.
self.write_toml(p)
self.write_toml(parsed_pipfile)
return newly_added, category, normalized_name

def src_name_from_url(self, index_url):
Expand Down
20 changes: 10 additions & 10 deletions pipenv/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ def make_requirement(name=None, entry=None):
def clean_initial_dict(cls, entry_dict):
from pipenv.patched.pip._vendor.packaging.requirements import Requirement

entry_dict.get("version", "")
version = entry_dict.get("version", "")
if isinstance(version, Requirement):
version = str(version.specifier)
Expand Down Expand Up @@ -250,6 +249,8 @@ def marker_to_str(marker):

@cached_property
def get_cleaned_dict(self):
from pipenv.utils.constants import VCS_LIST

self.validate_constraints()
if self.entry.extras != self.lockfile_entry.extras:
entry_extras = list(self.entry.extras)
Expand All @@ -268,6 +269,12 @@ def get_cleaned_dict(self):
_, self.entry_dict = self.get_markers_from_dict(self.entry_dict)
if self.resolver.index_lookup.get(self.name):
self.entry_dict["index"] = self.resolver.index_lookup[self.name]

# Handle VCS entries
for key in VCS_LIST:
if key in self.lockfile_dict:
self.entry_dict[key] = self.lockfile_dict[key]
self.entry_dict.pop("version", None)
return self.entry_dict

@property
Expand All @@ -290,9 +297,7 @@ def pipfile_entry(self):

@property
def entry(self):
if self._entry is None:
self._entry = self.make_requirement(self.name, self.entry_dict)
return self._entry
return self.make_requirement(self.name, self.lockfile_dict)

@property
def normalized_name(self):
Expand Down Expand Up @@ -548,18 +553,13 @@ def __getattribute__(self, key):

def clean_results(results, resolver, project, category):
from pipenv.utils.dependencies import (
get_lockfile_section_using_pipfile_category,
translate_markers,
)

if not project.lockfile_exists:
return results
lockfile = project.lockfile_content
lockfile_section = get_lockfile_section_using_pipfile_category(category)
reverse_deps = project.environment.reverse_dependencies()
new_results = [
r for r in results if r["name"] not in lockfile.get(lockfile_section, {})
]
new_results = []
for result in results:
name = result.get("name")
entry_dict = result.copy()
Expand Down
Loading

0 comments on commit b872d0b

Please sign in to comment.