prompt: 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()
  
  ```
  
golden_diff: 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
  
verification_info: {"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"}]}