Repository: popsim-consortium/stdpopsim (language: python)

Issue:
utils.download() should report the url when a download fails
E.g. if a url cannot be found, the following error appears from the test suite.
```
Traceback (most recent call last):
  File "/usr/share/miniconda/envs/stdpopsim/lib/python3.9/site-packages/nose/suite.py", line 210, in run
    self.setUp()
  File "/usr/share/miniconda/envs/stdpopsim/lib/python3.9/site-packages/nose/suite.py", line 293, in setUp
    self.setupContext(ancestor)
  File "/usr/share/miniconda/envs/stdpopsim/lib/python3.9/site-packages/nose/suite.py", line 316, in setupContext
    try_run(context, names)
  File "/usr/share/miniconda/envs/stdpopsim/lib/python3.9/site-packages/nose/util.py", line 471, in try_run
    return func()
  File "/home/runner/work/stdpopsim/stdpopsim/tests/test_genetic_maps.py", line 37, in setUpModule
    utils.download(genetic_map.url, local_file)
  File "/home/runner/work/stdpopsim/stdpopsim/stdpopsim/utils.py", line 70, in download
    with urllib.request.urlopen(url, timeout=30) as f_in:
  File "/usr/share/miniconda/envs/stdpopsim/lib/python3.9/urllib/request.py", line 214, in urlopen
    return opener.open(url, data, timeout)
  File "/usr/share/miniconda/envs/stdpopsim/lib/python3.9/urllib/request.py", line 523, in open
    response = meth(req, response)
  File "/usr/share/miniconda/envs/stdpopsim/lib/python3.9/urllib/request.py", line 632, in http_response
    response = self.parent.error(
  File "/usr/share/miniconda/envs/stdpopsim/lib/python3.9/urllib/request.py", line 561, in error
    return self._call_chain(*args)
  File "/usr/share/miniconda/envs/stdpopsim/lib/python3.9/urllib/request.py", line 494, in _call_chain
    result = func(*args)
  File "/usr/share/miniconda/envs/stdpopsim/lib/python3.9/urllib/request.py", line 641, in http_error_default
    raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 404: Not Found
```

Probably we should catch the exception in `utils.download()`, print the url, then reraise the exception. Or maybe there's a way to add information to an exception before reraising?


Discussion / hints:

Fix patch:
diff --git a/stdpopsim/utils.py b/stdpopsim/utils.py
index 447f2b1be..f4cb2ea67 100644
--- a/stdpopsim/utils.py
+++ b/stdpopsim/utils.py
@@ -77,10 +77,14 @@ def download(url, filename):
     """
     Download url to the specified local file.
     """
-    # TODO: what is a sensible timeout here?
-    with urllib.request.urlopen(url, timeout=30) as f_in:
-        with open(filename, "wb") as f_out:
-            shutil.copyfileobj(f_in, f_out)
+    try:
+        with urllib.request.urlopen(url, timeout=30) as f_in:
+            with open(filename, "wb") as f_out:
+                shutil.copyfileobj(f_in, f_out)
+    except urllib.error.HTTPError as e:
+        # Amend the error message to include the url.
+        e.msg += f": {url}"
+        raise e
 
 
 def sha256(filename):


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 6bce2e273..f2c58191e 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -216,9 +216,9 @@ def test_download_nonexistent(self):
                 input_filename.resolve().as_uri(),  # local file
                 "http://example.com/nonexistant",  # remote file
             ):
-                with pytest.raises(OSError):
+                with pytest.raises(OSError, match="nonexistant"):
                     utils.download(url, output_filename)
-                assert not (output_filename.exists())
+                assert not output_filename.exists()
 
 
 class TestSha256: