diff --git a/scrapy/pipelines/files.py b/scrapy/pipelines/files.py index f4f34aaf7..38f8537d6 100644 --- a/scrapy/pipelines/files.py +++ b/scrapy/pipelines/files.py @@ -450,9 +450,16 @@ class FilesPipeline(MediaPipeline): *, crawler: Crawler | None = None, ): - store_uri = _to_string(store_uri) - if not store_uri: - raise NotConfigured + if not (store_uri and (store_uri := _to_string(store_uri))): + from scrapy.pipelines.images import ImagesPipeline # noqa: PLC0415 + + setting_name = ( + "IMAGES_STORE" if isinstance(self, ImagesPipeline) else "FILES_STORE" + ) + raise NotConfigured( + f"{setting_name} setting must be set to a valid path (not empty) " + f"to enable {self.__class__.__name__}." + ) if crawler is not None: if settings is not None: diff --git a/tests/test_pipeline_files.py b/tests/test_pipeline_files.py index db64589af..5276636fc 100644 --- a/tests/test_pipeline_files.py +++ b/tests/test_pipeline_files.py @@ -20,6 +20,7 @@ import pytest from itemadapter import ItemAdapter from twisted.internet.defer import inlineCallbacks +from scrapy.exceptions import NotConfigured from scrapy.http import Request, Response from scrapy.item import Field, Item from scrapy.pipelines.files import ( @@ -29,6 +30,7 @@ from scrapy.pipelines.files import ( GCSFilesStore, S3FilesStore, ) +from scrapy.settings import Settings from scrapy.utils.spider import DefaultSpider from scrapy.utils.test import get_crawler from tests.mockserver.ftp import MockFTPServer @@ -804,3 +806,14 @@ class TestBuildFromCrawler: assert len(w) == 0 assert pipe.store assert pipe._from_crawler_called + + +@pytest.mark.parametrize("store", [None, ""]) +def test_files_pipeline_raises_notconfigured_when_files_store_invalid(store): + settings = Settings() + settings.clear() + settings.set("FILES_STORE", store, priority="cmdline") + crawler = get_crawler(settings_dict=settings) + + with pytest.raises(NotConfigured): + FilesPipeline.from_crawler(crawler)