diff --git a/poetry/core/packages/dependency.py b/poetry/core/packages/dependency.py index f17485fd7..4dc471573 100644 --- a/poetry/core/packages/dependency.py +++ b/poetry/core/packages/dependency.py @@ -395,21 +395,21 @@ def __eq__(self, other): # type: (Any) -> bool if not isinstance(other, Dependency): return NotImplemented - return ( - self.is_same_package_as(other) - and self._constraint == other.constraint - and self._extras == other.extras - ) + is_direct_origin = self.is_vcs() or self.is_directory() or self.is_file() or self.is_url() + + # "constraint" is implicitly given for direct origin dependencies and might not + # be set yet ("*"). Thus, it shouldn't be used to determine if two direct origin + # dependencies are equal. + # Checkoing is_direct_origin for one dependency is sufficient because + # super().__eq__() returns False for different origins. + return super().__eq__(other) and (is_direct_origin or self._constraint == other.constraint) def __ne__(self, other): # type: (Any) -> bool return not self == other def __hash__(self): # type: () -> int - return ( - super(Dependency, self).__hash__() - ^ hash(self._constraint) - ^ hash(self._extras) - ) + # don't include _constraint in hash because it is mutable! + return super().__hash__() def __str__(self): # type: () -> str if self.is_root: diff --git a/poetry/core/packages/directory_dependency.py b/poetry/core/packages/directory_dependency.py index ac1193904..df7c6f856 100644 --- a/poetry/core/packages/directory_dependency.py +++ b/poetry/core/packages/directory_dependency.py @@ -133,6 +133,3 @@ def __str__(self): # type: () -> str return "{} ({} {})".format( self._pretty_name, self._pretty_constraint, self._path.as_posix() ) - - def __hash__(self): # type: () -> int - return hash((self._name, self._full_path.as_posix())) diff --git a/poetry/core/packages/file_dependency.py b/poetry/core/packages/file_dependency.py index be79fd90d..8489e034b 100644 --- a/poetry/core/packages/file_dependency.py +++ b/poetry/core/packages/file_dependency.py @@ -118,6 +118,3 @@ def __str__(self): # type: () -> str return "{} ({} {})".format( self._pretty_name, self._pretty_constraint, self._path ) - - def __hash__(self): # type: () -> int - return hash((self._name, self._full_path)) diff --git a/poetry/core/packages/package.py b/poetry/core/packages/package.py index 02c289386..3cb366323 100644 --- a/poetry/core/packages/package.py +++ b/poetry/core/packages/package.py @@ -410,14 +410,14 @@ def clone(self): # type: () -> "Package" clone.__dict__ = copy.deepcopy(self.__dict__) return clone - def __hash__(self): # type: () -> int - return super(Package, self).__hash__() ^ hash(self._version) - def __eq__(self, other): # type: (Package) -> bool if not isinstance(other, Package): return NotImplemented - return self.is_same_package_as(other) and self._version == other.version + return super().__eq__(other) and self._version == other.version + + def __hash__(self) -> int: + return super().__hash__() ^ hash(self._version) def __str__(self): # type: () -> str return "{} ({})".format(self.complete_name, self.full_pretty_version) diff --git a/poetry/core/packages/specification.py b/poetry/core/packages/specification.py index 70b88f193..8b1b40515 100644 --- a/poetry/core/packages/specification.py +++ b/poetry/core/packages/specification.py @@ -1,3 +1,4 @@ +from typing import Any from typing import FrozenSet from typing import List from typing import Optional @@ -101,18 +102,24 @@ def is_same_package_as(self, other): # type: ("PackageSpecification") -> bool return True + def __eq__(self, other): # type: (Any) -> bool + if not isinstance(other, PackageSpecification): + return NotImplemented + return self.is_same_package_as(other) + def __hash__(self): # type: () -> int - if not self._source_type: - return hash(self._name) - - return ( - hash(self._name) - ^ hash(self._source_type) - ^ hash(self._source_url) - ^ hash(self._source_reference) - ^ hash(self._source_resolved_reference) - ^ hash(self._features) - ) + result = hash(self.complete_name) # complete_name includes features + + if self._source_type: + # Don't include _source_reference and _source_resolved_reference in hash + # because two specs can be equal even if these attributes are not equal. + # (They must still meet certain conditions. See is_same_source_as().) + result ^= ( + hash(self._source_type) + ^ hash(self._source_url) + ) + + return result def __str__(self): # type: () -> str raise NotImplementedError() diff --git a/poetry/core/packages/url_dependency.py b/poetry/core/packages/url_dependency.py index 2d4ce5dd4..e32f1a824 100644 --- a/poetry/core/packages/url_dependency.py +++ b/poetry/core/packages/url_dependency.py @@ -80,6 +80,3 @@ def with_constraint(self, constraint): # type: ("BaseConstraint") -> URLDepende def __str__(self): # type: () -> str return "{} ({} url)".format(self._pretty_name, self._pretty_constraint) - - def __hash__(self): # type: () -> int - return hash((self._name, self._url)) diff --git a/poetry/core/packages/vcs_dependency.py b/poetry/core/packages/vcs_dependency.py index 2800644aa..c668281c3 100644 --- a/poetry/core/packages/vcs_dependency.py +++ b/poetry/core/packages/vcs_dependency.py @@ -160,6 +160,3 @@ def __str__(self): # type: () -> str reference += " rev {}".format(self._rev) return "{} ({} {})".format(self._pretty_name, self._constraint, reference) - - def __hash__(self): # type: () -> int - return hash((self._name, self._vcs, self._branch, self._tag, self._rev))