failure.py (poetry-1.1.15) | : | failure.py (poetry-1.2.0) | ||
---|---|---|---|---|
from typing import Dict | from __future__ import annotations | |||
from typing import List | ||||
from typing import Tuple | from typing import TYPE_CHECKING | |||
from typing import cast | ||||
from poetry.core.semver import parse_constraint | ||||
from poetry.core.semver.helpers import parse_constraint | ||||
from .incompatibility import Incompatibility | ||||
from .incompatibility_cause import ConflictCause | from poetry.mixology.incompatibility_cause import ConflictCause | |||
from .incompatibility_cause import PythonCause | from poetry.mixology.incompatibility_cause import PythonCause | |||
if TYPE_CHECKING: | ||||
from poetry.mixology.incompatibility import Incompatibility | ||||
class SolveFailure(Exception): | class SolveFailure(Exception): | |||
def __init__(self, incompatibility): # type: (Incompatibility) -> None | def __init__(self, incompatibility: Incompatibility) -> None: | |||
self._incompatibility = incompatibility | self._incompatibility = incompatibility | |||
@property | @property | |||
def message(self): | def message(self) -> str: | |||
return str(self) | return str(self) | |||
def __str__(self): | def __str__(self) -> str: | |||
return _Writer(self._incompatibility).write() | return _Writer(self._incompatibility).write() | |||
class _Writer: | class _Writer: | |||
def __init__(self, root): # type: (Incompatibility) -> None | def __init__(self, root: Incompatibility) -> None: | |||
self._root = root | self._root = root | |||
self._derivations = {} # type: Dict[Incompatibility, int] | self._derivations: dict[Incompatibility, int] = {} | |||
self._lines = [] # type: List[Tuple[str, int]] | self._lines: list[tuple[str, int | None]] = [] | |||
self._line_numbers = {} # type: Dict[Incompatibility, int] | self._line_numbers: dict[Incompatibility, int] = {} | |||
self._count_derivations(self._root) | self._count_derivations(self._root) | |||
def write(self): | def write(self) -> str: | |||
buffer = [] | buffer = [] | |||
required_python_version_notification = False | required_python_version_notification = False | |||
for incompatibility in self._root.external_incompatibilities: | for incompatibility in self._root.external_incompatibilities: | |||
if isinstance(incompatibility.cause, PythonCause): | if isinstance(incompatibility.cause, PythonCause): | |||
if not required_python_version_notification: | if not required_python_version_notification: | |||
buffer.append( | buffer.append( | |||
"The current project's Python requirement ({}) " | "The current project's Python requirement" | |||
"is not compatible with some of the required " | f" ({incompatibility.cause.root_python_version}) is not" | |||
"packages Python requirement:".format( | " compatible with some of the required packages Python" | |||
incompatibility.cause.root_python_version | " requirement:" | |||
) | ||||
) | ) | |||
required_python_version_notification = True | required_python_version_notification = True | |||
root_constraint = parse_constraint( | root_constraint = parse_constraint( | |||
incompatibility.cause.root_python_version | incompatibility.cause.root_python_version | |||
) | ) | |||
constraint = parse_constraint(incompatibility.cause.python_versi on) | constraint = parse_constraint(incompatibility.cause.python_versi on) | |||
buffer.append( | buffer.append( | |||
" - {} requires Python {}, so it will not be satisfied for | f" - {incompatibility.terms[0].dependency.name} requires Py | |||
Python {}".format( | thon" | |||
incompatibility.terms[0].dependency.name, | f" {incompatibility.cause.python_version}, so it will not be | |||
incompatibility.cause.python_version, | " | |||
root_constraint.difference(constraint), | f" satisfied for Python {root_constraint.difference(constrai | |||
) | nt)}" | |||
) | ) | |||
if required_python_version_notification: | if required_python_version_notification: | |||
buffer.append("") | buffer.append("") | |||
if isinstance(self._root.cause, ConflictCause): | if isinstance(self._root.cause, ConflictCause): | |||
self._visit(self._root, {}) | self._visit(self._root) | |||
else: | else: | |||
self._write( | self._write(self._root, f"Because {self._root}, version solving fail | |||
self._root, "Because {}, version solving failed.".format(self._r | ed.") | |||
oot) | ||||
) | ||||
padding = ( | padding = ( | |||
0 | 0 | |||
if not self._line_numbers | if not self._line_numbers | |||
else len("({}) ".format(list(self._line_numbers.values())[-1])) | else len(f"({list(self._line_numbers.values())[-1]}) ") | |||
) | ) | |||
last_was_empty = False | last_was_empty = False | |||
for line in self._lines: | for line in self._lines: | |||
message = line[0] | message = line[0] | |||
if not message: | if not message: | |||
if not last_was_empty: | if not last_was_empty: | |||
buffer.append("") | buffer.append("") | |||
last_was_empty = True | last_was_empty = True | |||
continue | continue | |||
last_was_empty = False | last_was_empty = False | |||
number = line[-1] | number = line[-1] | |||
if number is not None: | if number is not None: | |||
message = "({})".format(number).ljust(padding) + message | message = f"({number})".ljust(padding) + message | |||
else: | else: | |||
message = " " * padding + message | message = " " * padding + message | |||
buffer.append(message) | buffer.append(message) | |||
return "\n".join(buffer) | return "\n".join(buffer) | |||
def _write( | def _write( | |||
self, incompatibility, message, numbered=False | self, incompatibility: Incompatibility, message: str, numbered: bool = F | |||
): # type: (Incompatibility, str, bool) -> None | alse | |||
) -> None: | ||||
if numbered: | if numbered: | |||
number = len(self._line_numbers) + 1 | number = len(self._line_numbers) + 1 | |||
self._line_numbers[incompatibility] = number | self._line_numbers[incompatibility] = number | |||
self._lines.append((message, number)) | self._lines.append((message, number)) | |||
else: | else: | |||
self._lines.append((message, None)) | self._lines.append((message, None)) | |||
def _visit( | def _visit( | |||
self, incompatibility, details_for_incompatibility, conclusion=False | self, | |||
): # type: (Incompatibility, Dict, bool) -> None | incompatibility: Incompatibility, | |||
conclusion: bool = False, | ||||
) -> None: | ||||
numbered = conclusion or self._derivations[incompatibility] > 1 | numbered = conclusion or self._derivations[incompatibility] > 1 | |||
conjunction = "So," if conclusion or incompatibility == self._root else "And" | conjunction = "So," if conclusion or incompatibility == self._root else "And" | |||
incompatibility_string = str(incompatibility) | incompatibility_string = str(incompatibility) | |||
cause = incompatibility.cause # type: ConflictCause | cause: ConflictCause = cast(ConflictCause, incompatibility.cause) | |||
details_for_cause = {} | ||||
if isinstance(cause.conflict.cause, ConflictCause) and isinstance( | if isinstance(cause.conflict.cause, ConflictCause) and isinstance( | |||
cause.other.cause, ConflictCause | cause.other.cause, ConflictCause | |||
): | ): | |||
conflict_line = self._line_numbers.get(cause.conflict) | conflict_line = self._line_numbers.get(cause.conflict) | |||
other_line = self._line_numbers.get(cause.other) | other_line = self._line_numbers.get(cause.other) | |||
if conflict_line is not None and other_line is not None: | if conflict_line is not None and other_line is not None: | |||
reason = cause.conflict.and_to_string( | ||||
cause.other, conflict_line, other_line | ||||
) | ||||
self._write( | self._write( | |||
incompatibility, | incompatibility, | |||
"Because {}, {}.".format( | f"Because {reason}, {incompatibility_string}.", | |||
cause.conflict.and_to_string( | ||||
cause.other, details_for_cause, conflict_line, other | ||||
_line | ||||
), | ||||
incompatibility_string, | ||||
), | ||||
numbered=numbered, | numbered=numbered, | |||
) | ) | |||
elif conflict_line is not None or other_line is not None: | elif conflict_line is not None or other_line is not None: | |||
if conflict_line is not None: | if conflict_line is not None: | |||
with_line = cause.conflict | with_line = cause.conflict | |||
without_line = cause.other | without_line = cause.other | |||
line = conflict_line | line = conflict_line | |||
else: | elif other_line is not None: | |||
with_line = cause.other | with_line = cause.other | |||
without_line = cause.conflict | without_line = cause.conflict | |||
line = other_line | line = other_line | |||
self._visit(without_line, details_for_cause) | self._visit(without_line) | |||
self._write( | self._write( | |||
incompatibility, | incompatibility, | |||
"{} because {} ({}), {}.".format( | f"{conjunction} because {with_line!s} ({line})," | |||
conjunction, str(with_line), line, incompatibility_strin | f" {incompatibility_string}.", | |||
g | ||||
), | ||||
numbered=numbered, | numbered=numbered, | |||
) | ) | |||
else: | else: | |||
single_line_conflict = self._is_single_line(cause.conflict.cause ) | single_line_conflict = self._is_single_line(cause.conflict.cause ) | |||
single_line_other = self._is_single_line(cause.other.cause) | single_line_other = self._is_single_line(cause.other.cause) | |||
if single_line_other or single_line_conflict: | if single_line_other or single_line_conflict: | |||
first = cause.conflict if single_line_other else cause.other | first = cause.conflict if single_line_other else cause.other | |||
second = cause.other if single_line_other else cause.conflic t | second = cause.other if single_line_other else cause.conflic t | |||
self._visit(first, details_for_cause) | self._visit(first) | |||
self._visit(second, details_for_cause) | self._visit(second) | |||
self._write( | self._write( | |||
incompatibility, | incompatibility, | |||
"Thus, {}.".format(incompatibility_string), | f"Thus, {incompatibility_string}.", | |||
numbered=numbered, | numbered=numbered, | |||
) | ) | |||
else: | else: | |||
self._visit(cause.conflict, {}, conclusion=True) | self._visit(cause.conflict, conclusion=True) | |||
self._lines.append(("", None)) | self._lines.append(("", None)) | |||
self._visit(cause.other, details_for_cause) | self._visit(cause.other) | |||
self._write( | self._write( | |||
incompatibility, | incompatibility, | |||
"{} because {} ({}), {}".format( | f"{conjunction} because" | |||
conjunction, | f" {cause.conflict!s} ({self._line_numbers[cause.conflic | |||
str(cause.conflict), | t]})," | |||
self._line_numbers[cause.conflict], | f" {incompatibility_string}", | |||
incompatibility_string, | ||||
), | ||||
numbered=numbered, | numbered=numbered, | |||
) | ) | |||
elif isinstance(cause.conflict.cause, ConflictCause) or isinstance( | elif isinstance(cause.conflict.cause, ConflictCause) or isinstance( | |||
cause.other.cause, ConflictCause | cause.other.cause, ConflictCause | |||
): | ): | |||
derived = ( | derived = ( | |||
cause.conflict | cause.conflict | |||
if isinstance(cause.conflict.cause, ConflictCause) | if isinstance(cause.conflict.cause, ConflictCause) | |||
else cause.other | else cause.other | |||
) | ) | |||
ext = ( | ext = ( | |||
cause.other | cause.other | |||
if isinstance(cause.conflict.cause, ConflictCause) | if isinstance(cause.conflict.cause, ConflictCause) | |||
else cause.conflict | else cause.conflict | |||
) | ) | |||
derived_line = self._line_numbers.get(derived) | derived_line = self._line_numbers.get(derived) | |||
if derived_line is not None: | if derived_line is not None: | |||
reason = ext.and_to_string(derived, None, derived_line) | ||||
self._write( | self._write( | |||
incompatibility, | incompatibility, | |||
"Because {}, {}.".format( | f"Because {reason}, {incompatibility_string}.", | |||
ext.and_to_string( | ||||
derived, details_for_cause, None, derived_line | ||||
), | ||||
incompatibility_string, | ||||
), | ||||
numbered=numbered, | numbered=numbered, | |||
) | ) | |||
elif self._is_collapsible(derived): | elif self._is_collapsible(derived): | |||
derived_cause = derived.cause # type: ConflictCause | derived_cause: ConflictCause = cast(ConflictCause, derived.cause ) | |||
if isinstance(derived_cause.conflict.cause, ConflictCause): | if isinstance(derived_cause.conflict.cause, ConflictCause): | |||
collapsed_derived = derived_cause.conflict | collapsed_derived = derived_cause.conflict | |||
collapsed_ext = derived_cause.other | ||||
else: | else: | |||
collapsed_derived = derived_cause.other | collapsed_derived = derived_cause.other | |||
if isinstance(derived_cause.conflict.cause, ConflictCause): | ||||
collapsed_ext = derived_cause.other | ||||
else: | ||||
collapsed_ext = derived_cause.conflict | collapsed_ext = derived_cause.conflict | |||
details_for_cause = {} | self._visit(collapsed_derived) | |||
reason = collapsed_ext.and_to_string(ext, None, None) | ||||
self._visit(collapsed_derived, details_for_cause) | ||||
self._write( | self._write( | |||
incompatibility, | incompatibility, | |||
"{} because {}, {}.".format( | f"{conjunction} because {reason}, {incompatibility_string}." | |||
conjunction, | , | |||
collapsed_ext.and_to_string(ext, details_for_cause, None | ||||
, None), | ||||
incompatibility_string, | ||||
), | ||||
numbered=numbered, | numbered=numbered, | |||
) | ) | |||
else: | else: | |||
self._visit(derived, details_for_cause) | self._visit(derived) | |||
self._write( | self._write( | |||
incompatibility, | incompatibility, | |||
"{} because {}, {}.".format( | f"{conjunction} because {ext!s}, {incompatibility_string}.", | |||
conjunction, str(ext), incompatibility_string | ||||
), | ||||
numbered=numbered, | numbered=numbered, | |||
) | ) | |||
else: | else: | |||
reason = cause.conflict.and_to_string(cause.other, None, None) | ||||
self._write( | self._write( | |||
incompatibility, | incompatibility, | |||
"Because {}, {}.".format( | f"Because {reason}, {incompatibility_string}.", | |||
cause.conflict.and_to_string( | ||||
cause.other, details_for_cause, None, None | ||||
), | ||||
incompatibility_string, | ||||
), | ||||
numbered=numbered, | numbered=numbered, | |||
) | ) | |||
def _is_collapsible(self, incompatibility): # type: (Incompatibility) -> bo ol | def _is_collapsible(self, incompatibility: Incompatibility) -> bool: | |||
if self._derivations[incompatibility] > 1: | if self._derivations[incompatibility] > 1: | |||
return False | return False | |||
cause = incompatibility.cause # type: ConflictCause | cause: ConflictCause = cast(ConflictCause, incompatibility.cause) | |||
if isinstance(cause.conflict.cause, ConflictCause) and isinstance( | if isinstance(cause.conflict.cause, ConflictCause) and isinstance( | |||
cause.other.cause, ConflictCause | cause.other.cause, ConflictCause | |||
): | ): | |||
return False | return False | |||
if not isinstance(cause.conflict.cause, ConflictCause) and not isinstanc e( | if not isinstance(cause.conflict.cause, ConflictCause) and not isinstanc e( | |||
cause.other.cause, ConflictCause | cause.other.cause, ConflictCause | |||
): | ): | |||
return False | return False | |||
complex = ( | complex = ( | |||
cause.conflict | cause.conflict | |||
if isinstance(cause.conflict.cause, ConflictCause) | if isinstance(cause.conflict.cause, ConflictCause) | |||
else cause.other | else cause.other | |||
) | ) | |||
return complex not in self._line_numbers | return complex not in self._line_numbers | |||
def _is_single_line(self, cause): # type: (ConflictCause) -> bool | def _is_single_line(self, cause: ConflictCause) -> bool: | |||
return not isinstance(cause.conflict.cause, ConflictCause) and not isins tance( | return not isinstance(cause.conflict.cause, ConflictCause) and not isins tance( | |||
cause.other.cause, ConflictCause | cause.other.cause, ConflictCause | |||
) | ) | |||
def _count_derivations(self, incompatibility): # type: (Incompatibility) -> None | def _count_derivations(self, incompatibility: Incompatibility) -> None: | |||
if incompatibility in self._derivations: | if incompatibility in self._derivations: | |||
self._derivations[incompatibility] += 1 | self._derivations[incompatibility] += 1 | |||
else: | else: | |||
self._derivations[incompatibility] = 1 | self._derivations[incompatibility] = 1 | |||
cause = incompatibility.cause | cause = incompatibility.cause | |||
if isinstance(cause, ConflictCause): | if isinstance(cause, ConflictCause): | |||
self._count_derivations(cause.conflict) | self._count_derivations(cause.conflict) | |||
self._count_derivations(cause.other) | self._count_derivations(cause.other) | |||
End of changes. 41 change blocks. | ||||
98 lines changed or deleted | 77 lines changed or added |