Skip to content

Commit

Permalink
solves the locally built git wheel wheres the ref? mystery
Browse files Browse the repository at this point in the history
  • Loading branch information
matteius authored and oz123 committed Oct 29, 2024
1 parent 2749a9d commit a65d4b2
Show file tree
Hide file tree
Showing 13 changed files with 190 additions and 143 deletions.
44 changes: 18 additions & 26 deletions news/6281.bugfix.rst
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
# This patch series improves Pipenv's reverse dependency handling, JSON output support, and upgrade routines, ensuring more accurate dependency management and better error handling.

## Key Changes

* **Reverse Dependency Graph**:
* ``graph --reverse`` now supports JSON output, consistent with ``pipdeptree``.
* Enhanced JSON-tree format for reverse dependencies, improving compatibility with JSON-processing tools.

* **Improved Upgrade Logic**:
* Pre-sync step added before conflict analysis to ensure accurate dependency resolution.
* Early conflict detection prevents incompatible upgrades, improving lock file integrity.
* Enhanced handling of reverse dependencies to avoid unintended conflicts during updates.

* **Refactoring & Consistent Output**:
* Replaced ``click.echo`` calls with ``console`` and ``err`` utilities for consistent output and error handling.
* Streamlined upgrade logic to reduce installation phases and improve performance.

## Bug Fixes

* Fixed incompatibility when using both ``--json`` and ``--json-tree`` flags simultaneously.
* Addressed #6281: Resolved transitive dependency conflicts (e.g., ``google-api-core`` vs. ``protobuf``).
* Updated tests to cover new JSON outputs and compatibility checks.

## Impact

These changes improve accuracy and reliability in complex dependency trees, ensuring robust updates and clearer error reporting.
Fix dependency resolution edge cases and versioning constraints handling:
* Allow JSON format options for ``--reverse`` dependency graph output matching pipdeptree
* Improve installation and upgrade routines to better handle dependencies
* Add ability to specify json output as pipdeptree does
* Add more consistent handling of VCS dependencies and references
* Fix synchronization of development and default dependencies during updates
* Ensure proper propagation of version constraints during updates
* Fix handling of ``~=`` and other version specifiers during updates

Key Changes:
* Improved reverse dependency analysis to catch conflicts earlier in resolution
* Better handling of VCS package lock data, preserving refs and subdirectories
* Fixed issue where VCS references could be lost in lock file when installed via commit hash
* Better handling of pipfile categories during installation and updates
* Corrected logic for development dependency resolution and constraint propagation
* Improved validation and preservation of version specifiers during updates

This improves stability when working with complex dependency trees and version constraints.
2 changes: 1 addition & 1 deletion pipenv/cli/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ def install(state, **kwargs):
editable_packages=state.installstate.editables,
site_packages=state.site_packages,
extra_pip_args=state.installstate.extra_pip_args,
categories=state.installstate.categories,
pipfile_categories=state.installstate.categories,
skip_lock=state.installstate.skip_lock,
)

Expand Down
3 changes: 1 addition & 2 deletions pipenv/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import os
import sys
from dataclasses import dataclass, field
from functools import cached_property
from pathlib import Path
from typing import Any, Dict, List, Optional, Set

Expand Down Expand Up @@ -228,7 +227,7 @@ def _get_pipfile_content(self) -> Dict[str, Any]:

return tomlkit_value_to_python(self.project.parsed_pipfile.get(self.category, {}))

@cached_property
@property
def get_cleaned_dict(self) -> Dict[str, Any]:
"""Create a cleaned dictionary representation of the entry."""
cleaned = {
Expand Down
132 changes: 66 additions & 66 deletions pipenv/routines/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def handle_new_packages(
system,
pypi_mirror,
extra_pip_args,
categories,
pipfile_categories,
perform_upgrades=True,
index=None,
):
Expand Down Expand Up @@ -77,8 +77,8 @@ def handle_new_packages(
sys.exit(1)

try:
if categories:
for category in categories:
if pipfile_categories:
for category in pipfile_categories:
added, cat, normalized_name = project.add_package_to_pipfile(
pkg_requirement, pkg_line, dev, category
)
Expand Down Expand Up @@ -120,7 +120,7 @@ def handle_new_packages(
pypi_mirror=pypi_mirror,
index_url=index,
extra_pip_args=extra_pip_args,
categories=categories,
categories=pipfile_categories,
)
return new_packages, True
except Exception:
Expand All @@ -144,27 +144,34 @@ def handle_lockfile(
categories,
):
"""Handle the lockfile, updating if necessary. Returns True if package updates were applied."""
if (project.lockfile_exists and not ignore_pipfile) and not skip_lock:
if (
(project.lockfile_exists and not ignore_pipfile)
and not skip_lock
and not packages
):
old_hash = project.get_lockfile_hash()
new_hash = project.calculate_pipfile_hash()
if new_hash != old_hash:
return handle_outdated_lockfile(
if deploy:
console.print(
f"Your Pipfile.lock ({old_hash}) is out of date. Expected: ({new_hash}).",
style="red",
)
raise exceptions.DeployException
handle_outdated_lockfile(
project,
packages,
old_hash=old_hash,
new_hash=new_hash,
system=system,
allow_global=allow_global,
deploy=deploy,
skip_lock=skip_lock,
pre=pre,
pypi_mirror=pypi_mirror,
categories=categories,
)
elif not project.lockfile_exists and not skip_lock:
handle_missing_lockfile(
project, system, allow_global, pre, pypi_mirror, categories
)
return False
handle_missing_lockfile(project, system, allow_global, pre, pypi_mirror)


def handle_outdated_lockfile(
Expand All @@ -174,20 +181,12 @@ def handle_outdated_lockfile(
new_hash,
system,
allow_global,
deploy,
skip_lock,
pre,
pypi_mirror,
categories,
):
"""Handle an outdated lockfile returning True if package updates were applied."""
from pipenv.routines.update import do_update

if deploy:
console.print(
f"Your Pipfile.lock ({old_hash}) is out of date. Expected: ({new_hash}).",
style="red",
)
raise exceptions.DeployException
if (system or allow_global) and not (project.s.PIPENV_VIRTUALENV):
err.print(
f"Pipfile.lock ({old_hash}) out of date, but installation uses --system so"
Expand All @@ -206,19 +205,18 @@ def handle_outdated_lockfile(
msg.format(old_hash, new_hash),
style="bold yellow",
)
do_update(
project,
packages=packages,
pre=pre,
system=system,
pypi_mirror=pypi_mirror,
categories=categories,
)
return True
return False
if not skip_lock:
do_lock(
project,
system=system,
pre=pre,
write=True,
pypi_mirror=pypi_mirror,
categories=None,
)


def handle_missing_lockfile(project, system, allow_global, pre, pypi_mirror, categories):
def handle_missing_lockfile(project, system, allow_global, pre, pypi_mirror):
if (system or allow_global) and not project.s.PIPENV_VIRTUALENV:
raise exceptions.PipenvOptionsError(
"--system",
Expand All @@ -237,7 +235,6 @@ def handle_missing_lockfile(project, system, allow_global, pre, pypi_mirror, cat
pre=pre,
write=True,
pypi_mirror=pypi_mirror,
categories=categories,
)


Expand All @@ -256,7 +253,7 @@ def do_install(
deploy=False,
site_packages=None,
extra_pip_args=None,
categories=None,
pipfile_categories=None,
skip_lock=False,
):
requirements_directory = fileutils.create_tracked_tempdir(
Expand All @@ -266,6 +263,11 @@ def do_install(
packages = packages if packages else []
editable_packages = editable_packages if editable_packages else []
package_args = [p for p in packages if p] + [p for p in editable_packages if p]
new_packages = []
if dev and not pipfile_categories:
pipfile_categories = ["dev-packages"]
elif not pipfile_categories:
pipfile_categories = ["packages"]

ensure_project(
project,
Expand All @@ -276,59 +278,59 @@ def do_install(
skip_requirements=False,
pypi_mirror=pypi_mirror,
site_packages=site_packages,
categories=categories,
)

do_install_validations(
project,
package_args,
requirements_directory,
dev=dev,
system=system,
ignore_pipfile=ignore_pipfile,
requirementstxt=requirementstxt,
pre=pre,
deploy=deploy,
categories=categories,
skip_lock=skip_lock,
pipfile_categories=pipfile_categories,
)

packages_installed = do_init(
do_init(
project,
package_args,
system=system,
allow_global=system,
deploy=deploy,
pypi_mirror=pypi_mirror,
categories=categories,
skip_lock=skip_lock,
categories=pipfile_categories,
)

new_packages, _packages_installed = handle_new_packages(
do_install_validations(
project,
packages,
editable_packages,
package_args,
requirements_directory,
dev=dev,
pre=pre,
system=system,
pypi_mirror=pypi_mirror,
extra_pip_args=extra_pip_args,
categories=categories,
perform_upgrades=not (packages_installed or skip_lock),
index=index,
ignore_pipfile=ignore_pipfile,
requirementstxt=requirementstxt,
pre=pre,
deploy=deploy,
categories=pipfile_categories,
skip_lock=skip_lock,
)
if packages_installed or _packages_installed:
sys.exit(0)
if not deploy:
new_packages, _ = handle_new_packages(
project,
packages,
editable_packages,
dev=dev,
pre=pre,
system=system,
pypi_mirror=pypi_mirror,
extra_pip_args=extra_pip_args,
pipfile_categories=pipfile_categories,
perform_upgrades=not skip_lock,
index=index,
)

try:
if dev: # Install both develop and default package categories from Pipfile.
pipfile_categories = ["default", "develop"]
do_install_dependencies(
project,
dev=dev,
allow_global=system,
requirements_dir=requirements_directory,
pypi_mirror=pypi_mirror,
extra_pip_args=extra_pip_args,
categories=categories,
categories=pipfile_categories,
skip_lock=skip_lock,
)
except Exception as e:
Expand Down Expand Up @@ -680,16 +682,16 @@ def do_init(
deploy=False,
pre=False,
pypi_mirror=None,
categories=None,
skip_lock=False,
categories=None,
):
"""Initialize the project, ensuring that the Pipfile and Pipfile.lock are in place.
Returns True if packages were updated + installed.
"""
if not deploy:
ensure_pipfile(project, system=system)

packages_updated = handle_lockfile(
handle_lockfile(
project,
packages,
ignore_pipfile=ignore_pipfile,
Expand All @@ -707,5 +709,3 @@ def do_init(
"To activate this project's virtualenv, run [yellow]pipenv shell[/yellow].\n"
"Alternatively, run a command inside the virtualenv with [yellow]pipenv run[/yellow]."
)

return packages_updated
1 change: 0 additions & 1 deletion pipenv/routines/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ def do_sync(
pypi_mirror=pypi_mirror,
deploy=deploy,
system=system,
categories=categories,
)
do_install_dependencies(
project,
Expand Down
Loading

0 comments on commit a65d4b2

Please sign in to comment.