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

Fix a bug that non-wheel file requirements can be resolved successfully #4443

Merged
merged 6 commits into from
Aug 28, 2020
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
1 change: 1 addition & 0 deletions news/4386.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a bug that non-wheel file requirements can be resolved successfully.
1 change: 1 addition & 0 deletions news/4441.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add the missing ``--system`` option to ``pipenv sync``.
2 changes: 2 additions & 0 deletions news/4443.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add a new option pair ``--header/--no-header`` to ``pipenv lock`` command,
which adds a header to the generated requirements.txt
1 change: 1 addition & 0 deletions pipenv/cli/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ def lock_options(f):
f = install_base_options(f)
f = lock_dev_option(f)
f = emit_requirements_flag(f)
f = emit_requirements_header_flag(f)
f = dev_only_flag(f)
return f

Expand Down
8 changes: 4 additions & 4 deletions pipenv/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -841,15 +841,15 @@ def do_install_dependencies(
dev = dev or dev_only
deps_list = list(lockfile.get_requirements(dev=dev, only=dev_only))
if emit_requirements:
index_args = prepare_pip_source_args(get_source_list(pypi_mirror=pypi_mirror, project=project))
index_args = prepare_pip_source_args(
get_source_list(pypi_mirror=pypi_mirror, project=project)
)
index_args = " ".join(index_args).replace(" -", "\n-")
deps = [
req.as_line(sources=False, include_hashes=False) for req in deps_list
]
click.echo(index_args)
click.echo(
"\n".join(sorted(deps))
)
click.echo("\n".join(sorted(deps)))
sys.exit(0)
if concurrent:
nprocs = PIPENV_MAX_SUBPROCESS
Expand Down
66 changes: 11 additions & 55 deletions pipenv/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,55 +504,6 @@ def get_pipfile_constraint(self):
"""
if self.is_in_pipfile:
return self.pipfile_entry.as_ireq()
return self.constraint_from_parent_conflicts()

def constraint_from_parent_conflicts(self):
"""
Given a resolved entry with multiple parent dependencies with different
constraints, searches for the resolution that satisfies all of the parent
constraints.

:return: A new **InstallRequirement** satisfying all parent constraints
:raises: :exc:`~pipenv.exceptions.DependencyConflict` if resolution is impossible
"""
# ensure that we satisfy the parent dependencies of this dep
parent_dependencies = set()
has_mismatch = False
can_use_original = True
for p in self.parent_deps:
# updated dependencies should be satisfied since they were resolved already
if p.is_updated:
continue
# parents with no requirements can't conflict
if not p.requirements:
continue
entry_ref = p.get_dependency(self.name)
required = entry_ref.get("required_version", "*")
required = self.clean_specifier(required)
parent_requires = self.make_requirement(self.name, required)
parent_dependencies.add("{0} => {1} ({2})".format(p.name, self.name, required))
# use pre=True here or else prereleases dont satisfy constraints
if parent_requires.requirement.specifier and (
not parent_requires.requirement.specifier.contains(self.original_version, prereleases=True)
):
can_use_original = False
if parent_requires.requirement.specifier and (
not parent_requires.requirement.specifier.contains(self.updated_version, prereleases=True)
):
if not self.entry.editable and self.updated_version != self.original_version:
has_mismatch = True
if has_mismatch and not can_use_original:
from pipenv.exceptions import DependencyConflict
msg = (
"Cannot resolve {0} ({1}) due to conflicting parent dependencies: "
"\n\t{2}".format(
self.name, self.updated_version, "\n\t".join(parent_dependencies)
)
)
raise DependencyConflict(msg)
elif can_use_original:
return self.lockfile_entry.as_ireq()
return self.entry.as_ireq()

def validate_constraints(self):
"""
Expand All @@ -562,19 +513,24 @@ def validate_constraints(self):
:return: True if the constraints are satisfied by the resolution provided
:raises: :exc:`pipenv.exceptions.DependencyConflict` if the constraints dont exist
"""
from pipenv.exceptions import DependencyConflict
from pipenv.environments import is_verbose

constraints = self.get_constraints()
pinned_version = self.updated_version
for constraint in constraints:
try:
constraint.check_if_exists(False)
except Exception:
from pipenv.exceptions import DependencyConflict
from pipenv.environments import is_verbose
if not constraint.req:
continue
if pinned_version and not constraint.req.specifier.contains(
str(pinned_version), prereleases=True
):
if is_verbose():
print("Tried constraint: {0!r}".format(constraint), file=sys.stderr)
msg = (
"Cannot resolve conflicting version {0}{1} while {2}{3} is "
"locked.".format(
self.name, self.updated_specifier, self.old_name, self.old_specifiers
self.name, constraint.req.specifier,
self.name, self.updated_specifier
)
)
raise DependencyConflict(msg)
Expand Down