From 44f0fd0d37832f2af1b5ddeb246b7e2490dce82d Mon Sep 17 00:00:00 2001 From: Adrian Chaves Date: Wed, 29 Apr 2026 12:25:54 +0200 Subject: [PATCH] Solve issue making CI hang? --- scrapy/crawler.py | 5 +++++ tests/test_crawler.py | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/scrapy/crawler.py b/scrapy/crawler.py index 2915e375b..ace5f4237 100644 --- a/scrapy/crawler.py +++ b/scrapy/crawler.py @@ -278,6 +278,11 @@ class Crawler: if self.engine is None: return + # During shutdown callbacks, graceful stop may be re-entered after + # the engine has already switched to non-running state. + if mode == "graceful" and not self.engine.running: + return + try: await self.engine.stop_async(mode=mode) except RuntimeError as exc: diff --git a/tests/test_crawler.py b/tests/test_crawler.py index c4c28114c..75a02aadc 100644 --- a/tests/test_crawler.py +++ b/tests/test_crawler.py @@ -797,6 +797,26 @@ async def test_crawler_stop_async_invalid_mode() -> None: await crawler.stop_async(mode="invalid") # type: ignore[arg-type] +@coroutine_test +async def test_crawler_graceful_stop_non_running_engine_is_noop() -> None: + crawler = get_crawler(DefaultSpider) + crawler.crawling = True + + class DummyEngine: + running = False + called = False + + async def stop_async(self, *, mode: str = "graceful") -> None: + self.called = True + + dummy_engine = DummyEngine() + crawler.engine = dummy_engine # type: ignore[assignment] + + await crawler.stop_async(mode="graceful") + + assert dummy_engine.called is False + + @coroutine_test async def test_crawler_force_stop_falls_back_to_fast( caplog: pytest.LogCaptureFixture,