We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
about the signal retry_complete
I didn't find the singnal in the singnal list,how can I use it

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `scrapy/downloadermiddlewares/retry.py`
Content:
```
1 """
2 An extension to retry failed requests that are potentially caused by temporary
3 problems such as a connection timeout or HTTP 500 error.
4 
5 You can change the behaviour of this middleware by modifing the scraping settings:
6 RETRY_TIMES - how many times to retry a failed page
7 RETRY_HTTP_CODES - which HTTP response codes to retry
8 
9 Failed pages are collected on the scraping process and rescheduled at the end,
10 once the spider has finished crawling all regular (non failed) pages. Once
11 there is no more failed pages to retry this middleware sends a signal
12 (retry_complete), so other extensions could connect to that signal.
13 """
14 import logging
15 
16 from twisted.internet import defer
17 from twisted.internet.error import TimeoutError, DNSLookupError, \
18         ConnectionRefusedError, ConnectionDone, ConnectError, \
19         ConnectionLost, TCPTimedOutError
20 from twisted.web.client import ResponseFailed
21 
22 from scrapy.exceptions import NotConfigured
23 from scrapy.utils.response import response_status_message
24 from scrapy.core.downloader.handlers.http11 import TunnelError
25 from scrapy.utils.python import global_object_name
26 
27 logger = logging.getLogger(__name__)
28 
29 
30 class RetryMiddleware(object):
31 
32     # IOError is raised by the HttpCompression middleware when trying to
33     # decompress an empty response
34     EXCEPTIONS_TO_RETRY = (defer.TimeoutError, TimeoutError, DNSLookupError,
35                            ConnectionRefusedError, ConnectionDone, ConnectError,
36                            ConnectionLost, TCPTimedOutError, ResponseFailed,
37                            IOError, TunnelError)
38 
39     def __init__(self, settings):
40         if not settings.getbool('RETRY_ENABLED'):
41             raise NotConfigured
42         self.max_retry_times = settings.getint('RETRY_TIMES')
43         self.retry_http_codes = set(int(x) for x in settings.getlist('RETRY_HTTP_CODES'))
44         self.priority_adjust = settings.getint('RETRY_PRIORITY_ADJUST')
45 
46     @classmethod
47     def from_crawler(cls, crawler):
48         return cls(crawler.settings)
49 
50     def process_response(self, request, response, spider):
51         if request.meta.get('dont_retry', False):
52             return response
53         if response.status in self.retry_http_codes:
54             reason = response_status_message(response.status)
55             return self._retry(request, reason, spider) or response
56         return response
57 
58     def process_exception(self, request, exception, spider):
59         if isinstance(exception, self.EXCEPTIONS_TO_RETRY) \
60                 and not request.meta.get('dont_retry', False):
61             return self._retry(request, exception, spider)
62 
63     def _retry(self, request, reason, spider):
64         retries = request.meta.get('retry_times', 0) + 1
65 
66         retry_times = self.max_retry_times
67 
68         if 'max_retry_times' in request.meta:
69             retry_times = request.meta['max_retry_times']
70 
71         stats = spider.crawler.stats
72         if retries <= retry_times:
73             logger.debug("Retrying %(request)s (failed %(retries)d times): %(reason)s",
74                          {'request': request, 'retries': retries, 'reason': reason},
75                          extra={'spider': spider})
76             retryreq = request.copy()
77             retryreq.meta['retry_times'] = retries
78             retryreq.dont_filter = True
79             retryreq.priority = request.priority + self.priority_adjust
80 
81             if isinstance(reason, Exception):
82                 reason = global_object_name(reason.__class__)
83 
84             stats.inc_value('retry/count')
85             stats.inc_value('retry/reason_count/%s' % reason)
86             return retryreq
87         else:
88             stats.inc_value('retry/max_reached')
89             logger.debug("Gave up retrying %(request)s (failed %(retries)d times): %(reason)s",
90                          {'request': request, 'retries': retries, 'reason': reason},
91                          extra={'spider': spider})
92
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/scrapy/downloadermiddlewares/retry.py b/scrapy/downloadermiddlewares/retry.py
--- a/scrapy/downloadermiddlewares/retry.py
+++ b/scrapy/downloadermiddlewares/retry.py
@@ -7,9 +7,7 @@
 RETRY_HTTP_CODES - which HTTP response codes to retry
 
 Failed pages are collected on the scraping process and rescheduled at the end,
-once the spider has finished crawling all regular (non failed) pages. Once
-there is no more failed pages to retry this middleware sends a signal
-(retry_complete), so other extensions could connect to that signal.
+once the spider has finished crawling all regular (non failed) pages.
 """
 import logging

{"golden_diff": "diff --git a/scrapy/downloadermiddlewares/retry.py b/scrapy/downloadermiddlewares/retry.py\n--- a/scrapy/downloadermiddlewares/retry.py\n+++ b/scrapy/downloadermiddlewares/retry.py\n@@ -7,9 +7,7 @@\n RETRY_HTTP_CODES - which HTTP response codes to retry\n \n Failed pages are collected on the scraping process and rescheduled at the end,\n-once the spider has finished crawling all regular (non failed) pages. Once\n-there is no more failed pages to retry this middleware sends a signal\n-(retry_complete), so other extensions could connect to that signal.\n+once the spider has finished crawling all regular (non failed) pages.\n \"\"\"\n import logging\n", "issue": "about the signal retry_complete\nI didn't find the singnal in the singnal list,how can I use it\n", "before_files": [{"content": "\"\"\"\nAn extension to retry failed requests that are potentially caused by temporary\nproblems such as a connection timeout or HTTP 500 error.\n\nYou can change the behaviour of this middleware by modifing the scraping settings:\nRETRY_TIMES - how many times to retry a failed page\nRETRY_HTTP_CODES - which HTTP response codes to retry\n\nFailed pages are collected on the scraping process and rescheduled at the end,\nonce the spider has finished crawling all regular (non failed) pages. Once\nthere is no more failed pages to retry this middleware sends a signal\n(retry_complete), so other extensions could connect to that signal.\n\"\"\"\nimport logging\n\nfrom twisted.internet import defer\nfrom twisted.internet.error import TimeoutError, DNSLookupError, \\\n        ConnectionRefusedError, ConnectionDone, ConnectError, \\\n        ConnectionLost, TCPTimedOutError\nfrom twisted.web.client import ResponseFailed\n\nfrom scrapy.exceptions import NotConfigured\nfrom scrapy.utils.response import response_status_message\nfrom scrapy.core.downloader.handlers.http11 import TunnelError\nfrom scrapy.utils.python import global_object_name\n\nlogger = logging.getLogger(__name__)\n\n\nclass RetryMiddleware(object):\n\n    # IOError is raised by the HttpCompression middleware when trying to\n    # decompress an empty response\n    EXCEPTIONS_TO_RETRY = (defer.TimeoutError, TimeoutError, DNSLookupError,\n                           ConnectionRefusedError, ConnectionDone, ConnectError,\n                           ConnectionLost, TCPTimedOutError, ResponseFailed,\n                           IOError, TunnelError)\n\n    def __init__(self, settings):\n        if not settings.getbool('RETRY_ENABLED'):\n            raise NotConfigured\n        self.max_retry_times = settings.getint('RETRY_TIMES')\n        self.retry_http_codes = set(int(x) for x in settings.getlist('RETRY_HTTP_CODES'))\n        self.priority_adjust = settings.getint('RETRY_PRIORITY_ADJUST')\n\n    @classmethod\n    def from_crawler(cls, crawler):\n        return cls(crawler.settings)\n\n    def process_response(self, request, response, spider):\n        if request.meta.get('dont_retry', False):\n            return response\n        if response.status in self.retry_http_codes:\n            reason = response_status_message(response.status)\n            return self._retry(request, reason, spider) or response\n        return response\n\n    def process_exception(self, request, exception, spider):\n        if isinstance(exception, self.EXCEPTIONS_TO_RETRY) \\\n                and not request.meta.get('dont_retry', False):\n            return self._retry(request, exception, spider)\n\n    def _retry(self, request, reason, spider):\n        retries = request.meta.get('retry_times', 0) + 1\n\n        retry_times = self.max_retry_times\n\n        if 'max_retry_times' in request.meta:\n            retry_times = request.meta['max_retry_times']\n\n        stats = spider.crawler.stats\n        if retries <= retry_times:\n            logger.debug(\"Retrying %(request)s (failed %(retries)d times): %(reason)s\",\n                         {'request': request, 'retries': retries, 'reason': reason},\n                         extra={'spider': spider})\n            retryreq = request.copy()\n            retryreq.meta['retry_times'] = retries\n            retryreq.dont_filter = True\n            retryreq.priority = request.priority + self.priority_adjust\n\n            if isinstance(reason, Exception):\n                reason = global_object_name(reason.__class__)\n\n            stats.inc_value('retry/count')\n            stats.inc_value('retry/reason_count/%s' % reason)\n            return retryreq\n        else:\n            stats.inc_value('retry/max_reached')\n            logger.debug(\"Gave up retrying %(request)s (failed %(retries)d times): %(reason)s\",\n                         {'request': request, 'retries': retries, 'reason': reason},\n                         extra={'spider': spider})\n", "path": "scrapy/downloadermiddlewares/retry.py"}], "after_files": [{"content": "\"\"\"\nAn extension to retry failed requests that are potentially caused by temporary\nproblems such as a connection timeout or HTTP 500 error.\n\nYou can change the behaviour of this middleware by modifing the scraping settings:\nRETRY_TIMES - how many times to retry a failed page\nRETRY_HTTP_CODES - which HTTP response codes to retry\n\nFailed pages are collected on the scraping process and rescheduled at the end,\nonce the spider has finished crawling all regular (non failed) pages.\n\"\"\"\nimport logging\n\nfrom twisted.internet import defer\nfrom twisted.internet.error import TimeoutError, DNSLookupError, \\\n        ConnectionRefusedError, ConnectionDone, ConnectError, \\\n        ConnectionLost, TCPTimedOutError\nfrom twisted.web.client import ResponseFailed\n\nfrom scrapy.exceptions import NotConfigured\nfrom scrapy.utils.response import response_status_message\nfrom scrapy.core.downloader.handlers.http11 import TunnelError\nfrom scrapy.utils.python import global_object_name\n\nlogger = logging.getLogger(__name__)\n\n\nclass RetryMiddleware(object):\n\n    # IOError is raised by the HttpCompression middleware when trying to\n    # decompress an empty response\n    EXCEPTIONS_TO_RETRY = (defer.TimeoutError, TimeoutError, DNSLookupError,\n                           ConnectionRefusedError, ConnectionDone, ConnectError,\n                           ConnectionLost, TCPTimedOutError, ResponseFailed,\n                           IOError, TunnelError)\n\n    def __init__(self, settings):\n        if not settings.getbool('RETRY_ENABLED'):\n            raise NotConfigured\n        self.max_retry_times = settings.getint('RETRY_TIMES')\n        self.retry_http_codes = set(int(x) for x in settings.getlist('RETRY_HTTP_CODES'))\n        self.priority_adjust = settings.getint('RETRY_PRIORITY_ADJUST')\n\n    @classmethod\n    def from_crawler(cls, crawler):\n        return cls(crawler.settings)\n\n    def process_response(self, request, response, spider):\n        if request.meta.get('dont_retry', False):\n            return response\n        if response.status in self.retry_http_codes:\n            reason = response_status_message(response.status)\n            return self._retry(request, reason, spider) or response\n        return response\n\n    def process_exception(self, request, exception, spider):\n        if isinstance(exception, self.EXCEPTIONS_TO_RETRY) \\\n                and not request.meta.get('dont_retry', False):\n            return self._retry(request, exception, spider)\n\n    def _retry(self, request, reason, spider):\n        retries = request.meta.get('retry_times', 0) + 1\n\n        retry_times = self.max_retry_times\n\n        if 'max_retry_times' in request.meta:\n            retry_times = request.meta['max_retry_times']\n\n        stats = spider.crawler.stats\n        if retries <= retry_times:\n            logger.debug(\"Retrying %(request)s (failed %(retries)d times): %(reason)s\",\n                         {'request': request, 'retries': retries, 'reason': reason},\n                         extra={'spider': spider})\n            retryreq = request.copy()\n            retryreq.meta['retry_times'] = retries\n            retryreq.dont_filter = True\n            retryreq.priority = request.priority + self.priority_adjust\n\n            if isinstance(reason, Exception):\n                reason = global_object_name(reason.__class__)\n\n            stats.inc_value('retry/count')\n            stats.inc_value('retry/reason_count/%s' % reason)\n            return retryreq\n        else:\n            stats.inc_value('retry/max_reached')\n            logger.debug(\"Gave up retrying %(request)s (failed %(retries)d times): %(reason)s\",\n                         {'request': request, 'retries': retries, 'reason': reason},\n                         extra={'spider': spider})\n", "path": "scrapy/downloadermiddlewares/retry.py"}]}