Skip to content

Commit 7c70d4c

Browse files
samaloneyCadair
andauthored
Add callbacks for ftp downloads (#150)
* Add callbacks for ftp downloads * Add ftp callback tests and refactor http callback tests. --------- Co-authored-by: Stuart Mumford <[email protected]>
1 parent fc9a4e5 commit 7c70d4c

File tree

3 files changed

+79
-10
lines changed

3 files changed

+79
-10
lines changed

parfive/downloader.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,10 @@ async def _get_ftp(
841841

842842
await asyncio.gather(*download_workers)
843843
await downloaded_chunks_queue.join()
844+
845+
for callback in self.config.done_callbacks:
846+
callback(filepath, url, None)
847+
844848
return str(filepath)
845849

846850
except (Exception, asyncio.CancelledError) as e:
@@ -851,6 +855,11 @@ async def _get_ftp(
851855
# computed the filepath, so we have no file to cleanup
852856
if filepath is not None:
853857
remove_file(filepath)
858+
filepath = None
859+
860+
for callback in self.config.done_callbacks:
861+
callback(filepath, url, e)
862+
854863
raise FailedDownload(filepath_partial, url, e)
855864

856865
finally:

parfive/tests/test_downloader.py

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
from pathlib import Path
55
from tempfile import gettempdir
66
from unittest import mock
7-
from unittest.mock import patch
7+
from unittest.mock import MagicMock, patch
88

99
import aiohttp
1010
import pytest
11-
from aiohttp import ClientTimeout
11+
from aiohttp import ClientConnectorError, ClientTimeout
12+
from pytest_socket import SocketConnectBlockedError
1213

1314
import parfive
1415
from parfive.config import SessionConfig
@@ -495,24 +496,80 @@ def test_proxy_passed_as_kwargs_to_get(tmpdir, url, proxy):
495496
]
496497

497498

498-
def test_done_callback(httpserver, tmpdir):
499-
tmpdir = str(tmpdir)
499+
def test_http_callback_success(httpserver, tmpdir):
500+
# Test callback on successful download
500501
httpserver.serve_content(
501502
"SIMPLE = T", headers={"Content-Disposition": "attachment; filename=testfile.fits"}
502503
)
503504

504-
def done_callback(filepath, url, error):
505-
(Path(gettempdir()) / "callback.done").touch()
505+
cb = MagicMock()
506+
dl = Downloader(config=SessionConfig(done_callbacks=[cb]))
507+
dl.enqueue_file(httpserver.url, path=tmpdir, max_splits=None)
506508

507-
dl = Downloader(config=SessionConfig(done_callbacks=[done_callback]))
508-
dl.enqueue_file(httpserver.url, path=Path(tmpdir), max_splits=None)
509+
assert dl.queued_downloads == 1
510+
511+
dl.download()
512+
513+
assert cb.call_count == 1
514+
cb_path, cb_url, cb_status = cb.call_args[0]
515+
assert cb_path == tmpdir / "testfile.fits"
516+
assert httpserver.url == cb_url
517+
assert cb_status is None
518+
519+
520+
def test_http_callback_fail(httpserver, tmpdir):
521+
# Test callback on failed download
522+
cb = MagicMock()
523+
dl = Downloader(config=SessionConfig(done_callbacks=[cb]))
524+
url = "http://test.com/myfile.txt"
525+
dl.enqueue_file(url, path=tmpdir, max_splits=None)
526+
527+
assert dl.queued_downloads == 1
528+
529+
dl.download()
530+
531+
assert cb.call_count == 1
532+
cb_path, cb_url, cb_status = cb.call_args[0]
533+
assert cb_path is None
534+
assert url == cb_url
535+
assert isinstance(cb_status, (SocketConnectBlockedError, ClientConnectorError))
536+
537+
538+
@pytest.mark.allow_hosts(True)
539+
def test_ftp_callback_success(tmpdir):
540+
cb = MagicMock()
541+
dl = Downloader(config=SessionConfig(done_callbacks=[cb]))
542+
url = "ftp://ftp.swpc.noaa.gov/pub/warehouse/2011/2011_SRS.tar.gz"
543+
dl.enqueue_file(url, path=str(tmpdir))
544+
545+
assert dl.queued_downloads == 1
546+
547+
dl.download()
548+
549+
assert cb.call_count == 1
550+
cb_path, cb_url, cb_status = cb.call_args[0]
551+
assert cb_path == tmpdir / "2011_SRS.tar.gz"
552+
assert url == cb_url
553+
assert cb_status is None
554+
555+
556+
@mock.patch("aioftp.Client.context", side_effect=ConnectionRefusedError())
557+
def test_ftp_callback_error(tmpdir):
558+
# Download should fail as not marked with allowed hosts
559+
cb = MagicMock()
560+
dl = Downloader(config=SessionConfig(done_callbacks=[cb]))
561+
url = "ftp://127.0.0.1/nosuchfile.txt"
562+
dl.enqueue_file(url, path=str(tmpdir))
509563

510564
assert dl.queued_downloads == 1
511565

512566
dl.download()
513567

514-
assert (Path(gettempdir()) / "callback.done").exists()
515-
(Path(gettempdir()) / "callback.done").unlink()
568+
assert cb.call_count == 1
569+
cb_path, cb_url, cb_status = cb.call_args[0]
570+
assert cb_path is None
571+
assert cb_url == url
572+
assert isinstance(cb_status, ConnectionRefusedError)
516573

517574

518575
class CustomThread(threading.Thread):

setup.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,6 @@ ignore_missing_imports = True
119119

120120
[mypy-pytest.*]
121121
ignore_missing_imports = True
122+
123+
[mypy-pytest_socket.*]
124+
ignore_missing_imports = True

0 commit comments

Comments
 (0)