diff --git a/lib/spack/spack/fetch_strategy.py b/lib/spack/spack/fetch_strategy.py index 97d95c2fe96..c407f6908e3 100644 --- a/lib/spack/spack/fetch_strategy.py +++ b/lib/spack/spack/fetch_strategy.py @@ -42,7 +42,7 @@ from llnl.util.filesystem import ( working_dir, mkdirp, temp_rename, temp_cwd, get_single_file) from spack.util.compression import decompressor_for, extension -from spack.util.executable import which +from spack.util.executable import which, CommandNotFoundError from spack.util.string import comma_and, quote from spack.version import Version, ver @@ -267,7 +267,10 @@ def __init__(self, url=None, checksum=None, **kwargs): @property def curl(self): if not self._curl: - self._curl = which('curl', required=True) + try: + self._curl = which('curl', required=True) + except CommandNotFoundError as exc: + tty.error(str(exc)) return self._curl def source_id(self): diff --git a/lib/spack/spack/test/url_fetch.py b/lib/spack/spack/test/url_fetch.py index 37b4cdc214e..28b6c873853 100644 --- a/lib/spack/spack/test/url_fetch.py +++ b/lib/spack/spack/test/url_fetch.py @@ -18,6 +18,7 @@ from spack.stage import Stage from spack.version import ver import spack.util.crypto as crypto +import spack.util.executable @pytest.fixture(params=list(crypto.hashes.keys())) @@ -225,3 +226,29 @@ def test_candidate_urls(pkg_factory, url, urls, version, expected): pkg = pkg_factory(url, urls, fetch_options={'timeout': 60}) f = fs._from_merged_attrs(fs.URLFetchStrategy, pkg, version) assert f.extra_options == {'timeout': 60} + + +@pytest.mark.regression('19673') +def test_missing_curl(tmpdir, monkeypatch): + """Ensure a fetch involving missing curl package reports the error.""" + err_fmt = 'No such command {0}' + + def _which(*args, **kwargs): + err_msg = err_fmt.format(args[0]) + raise spack.util.executable.CommandNotFoundError(err_msg) + + # Patching the 'which' symbol imported by fetch_strategy works + # since it is too late in import processing to patch the defining + # (spack.util.executable) module's symbol. + monkeypatch.setattr(fs, 'which', _which) + + testpath = str(tmpdir) + url = 'http://github.com/spack/spack' + fetcher = fs.URLFetchStrategy(url=url) + assert fetcher is not None + + with pytest.raises(TypeError, match='object is not callable'): + with Stage(fetcher, path=testpath) as stage: + out = stage.fetch() + + assert err_fmt.format('curl') in out