bugfix: installed and installed_upstream should not assert (#30271)
				
					
				
			Fix bug introduced in #30191. `Spec.installed` and `Spec.installed_upstream` should just return `False` for abstract specs, as they can be called in that context. - [x] `Spec.installed` returns `False` now instead of asserting that the `Spec` is concrete. - [x] `Spec.installed_upstream` returns `False` now instead of asserting that the `Spec` is concrete. - [x] `Spec.installed_upstream` no longer caches its result, as install status seems like a bad thing to cache -- it can easily be invalidated. Calling code should use transactions if there are peformance issues, as with other places in Spack. - [x] add tests for `Spec.installed` and `Spec.installed_upstream`
This commit is contained in:
		| @@ -1197,7 +1197,6 @@ def __init__(self, spec_like=None, normal=False, | ||||
|         self._package_hash = None | ||||
|         self._dunder_hash = None | ||||
|         self._package = None | ||||
|         self._installed_upstream = None | ||||
| 
 | ||||
|         # Most of these are internal implementation details that can be | ||||
|         # set by internal Spack calls in the constructor. | ||||
| @@ -1562,8 +1561,9 @@ def installed(self): | ||||
|         Returns: | ||||
|             True if the package has been installed, False otherwise. | ||||
|         """ | ||||
|         msg = "a spec must be concrete to be queried for installation status" | ||||
|         assert self.concrete, msg | ||||
|         if not self.concrete: | ||||
|             return False | ||||
| 
 | ||||
|         try: | ||||
|             # If the spec is in the DB, check the installed | ||||
|             # attribute of the record | ||||
| @@ -1575,12 +1575,16 @@ def installed(self): | ||||
| 
 | ||||
|     @property | ||||
|     def installed_upstream(self): | ||||
|         msg = "a spec must be concrete to be queried for installation status" | ||||
|         assert self.concrete, msg | ||||
|         if getattr(self, '_installed_upstream', None) is None: | ||||
|             upstream, _ = spack.store.db.query_by_spec_hash(self.dag_hash()) | ||||
|             self._installed_upstream = upstream | ||||
|         return self._installed_upstream | ||||
|         """Whether the spec is installed in an upstream repository. | ||||
| 
 | ||||
|         Returns: | ||||
|             True if the package is installed in an upstream, False otherwise. | ||||
|         """ | ||||
|         if not self.concrete: | ||||
|             return False | ||||
| 
 | ||||
|         upstream, _ = spack.store.db.query_by_spec_hash(self.dag_hash()) | ||||
|         return upstream | ||||
| 
 | ||||
|     def traverse(self, **kwargs): | ||||
|         direction = kwargs.get('direction', 'children') | ||||
|   | ||||
| @@ -64,6 +64,36 @@ def upstream_and_downstream_db(tmpdir_factory, gen_mock_layout): | ||||
|         downstream_db, downstream_layout | ||||
| 
 | ||||
| 
 | ||||
| @pytest.mark.skipif(sys.platform == 'win32', | ||||
|                     reason="Upstreams currently unsupported on Windows") | ||||
| def test_spec_installed_upstream(upstream_and_downstream_db, config, monkeypatch): | ||||
|     """Test whether Spec.installed_upstream() works.""" | ||||
|     upstream_write_db, upstream_db, upstream_layout, \ | ||||
|         downstream_db, downstream_layout = upstream_and_downstream_db | ||||
| 
 | ||||
|     # a known installed spec should say that it's installed | ||||
|     mock_repo = MockPackageMultiRepo() | ||||
|     mock_repo.add_package('x', [], []) | ||||
| 
 | ||||
|     with spack.repo.use_repositories(mock_repo): | ||||
|         spec = spack.spec.Spec("x").concretized() | ||||
|         assert not spec.installed | ||||
|         assert not spec.installed_upstream | ||||
| 
 | ||||
|         upstream_write_db.add(spec, upstream_layout) | ||||
|         upstream_db._read() | ||||
| 
 | ||||
|         monkeypatch.setattr(spack.store, "db", downstream_db) | ||||
|         assert spec.installed | ||||
|         assert spec.installed_upstream | ||||
|         assert spec.copy().installed | ||||
| 
 | ||||
|     # an abstract spec should say it's not installed | ||||
|     spec = spack.spec.Spec("not-a-real-package") | ||||
|     assert not spec.installed | ||||
|     assert not spec.installed_upstream | ||||
| 
 | ||||
| 
 | ||||
| @pytest.mark.skipif(sys.platform == 'win32', | ||||
|                     reason="Upstreams currently unsupported on Windows") | ||||
| @pytest.mark.usefixtures('config') | ||||
|   | ||||
| @@ -284,7 +284,7 @@ def test_check_last_phase_error(install_mockery): | ||||
|     assert pkg.last_phase in err | ||||
| 
 | ||||
| 
 | ||||
| def test_installer_ensure_ready_errors(install_mockery): | ||||
| def test_installer_ensure_ready_errors(install_mockery, monkeypatch): | ||||
|     const_arg = installer_args(['trivial-install-test-package'], {}) | ||||
|     installer = create_installer(const_arg) | ||||
|     spec = installer.build_requests[0].pkg.spec | ||||
| @@ -300,14 +300,14 @@ def test_installer_ensure_ready_errors(install_mockery): | ||||
| 
 | ||||
|     # Force an upstream package error | ||||
|     spec.external_path, spec.external_modules = path, modules | ||||
|     spec._installed_upstream = True | ||||
|     monkeypatch.setattr(spack.spec.Spec, "installed_upstream", True) | ||||
|     msg = fmt.format('is upstream') | ||||
|     with pytest.raises(inst.UpstreamPackageError, match=msg): | ||||
|         installer._ensure_install_ready(spec.package) | ||||
| 
 | ||||
|     # Force an install lock error, which should occur naturally since | ||||
|     # we are calling an internal method prior to any lock-related setup | ||||
|     spec._installed_upstream = False | ||||
|     monkeypatch.setattr(spack.spec.Spec, "installed_upstream", False) | ||||
|     assert len(installer.locks) == 0 | ||||
|     with pytest.raises(inst.InstallLockError, match=fmt.format('not locked')): | ||||
|         installer._ensure_install_ready(spec.package) | ||||
|   | ||||
| @@ -1258,3 +1258,20 @@ def test_merge_anonymous_spec_with_named_spec(anonymous, named, expected): | ||||
|     changed = s.constrain(named) | ||||
|     assert changed | ||||
|     assert s == Spec(expected) | ||||
| 
 | ||||
| 
 | ||||
| def test_spec_installed(install_mockery, database): | ||||
|     """Test whether Spec.installed works.""" | ||||
|     # a known installed spec should say that it's installed | ||||
|     specs = database.query() | ||||
|     spec = specs[0] | ||||
|     assert spec.installed | ||||
|     assert spec.copy().installed | ||||
| 
 | ||||
|     # an abstract spec should say it's not installed | ||||
|     spec = Spec("not-a-real-package") | ||||
|     assert not spec.installed | ||||
| 
 | ||||
|     # 'a' is not in the mock DB and is not installed | ||||
|     spec = Spec("a").concretized() | ||||
|     assert not spec.installed | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Todd Gamblin
					Todd Gamblin