prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Cannot use contracts with inherited callbacks
  ### Description
  
  If you want to `scrapy check` a spider that has inherited methods, these methods' contracts will be ignored.
  
  ### Reproduce
  
  ```python
  class BaseSpider(Spider):
  
      def returns_request(self, response):
          """ method which returns request
          @url https://docs.scrapy.org/en/latest/
          @returns requests 1
          """
          return Request('http://scrapy.org', callback=self.returns_item)
  
  
  class DemoSpider(BaseSpider):
      name = 'demo_spider'
  ```
  
  And then run `scrapy check`.
  
  You'll get the following output:
  
  ```
  ----------------------------------------------------------------------
  Ran 0 contracts in 0.000s
  
  OK
  ```
  
  ### Reason
  
  `ContractsManager.tested_methods_from_spidercls` uses `vars(spidercls).items()` to get methods.
  
  ### Solution
  
  Use `inspect.getmembers(spidercls)` instead.
  
  --- 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/contracts/__init__.py`
  Content:
  ```
  1 import sys
  2 import re
  3 from functools import wraps
  4 from unittest import TestCase
  5 
  6 from scrapy.http import Request
  7 from scrapy.utils.spider import iterate_spider_output
  8 from scrapy.utils.python import get_spec
  9 
  10 
  11 class ContractsManager(object):
  12     contracts = {}
  13 
  14     def __init__(self, contracts):
  15         for contract in contracts:
  16             self.contracts[contract.name] = contract
  17 
  18     def tested_methods_from_spidercls(self, spidercls):
  19         methods = []
  20         for key, value in vars(spidercls).items():
  21             if (callable(value) and value.__doc__ and
  22                     re.search(r'^\s*@', value.__doc__, re.MULTILINE)):
  23                 methods.append(key)
  24 
  25         return methods
  26 
  27     def extract_contracts(self, method):
  28         contracts = []
  29         for line in method.__doc__.split('\n'):
  30             line = line.strip()
  31 
  32             if line.startswith('@'):
  33                 name, args = re.match(r'@(\w+)\s*(.*)', line).groups()
  34                 args = re.split(r'\s+', args)
  35 
  36                 contracts.append(self.contracts[name](method, *args))
  37 
  38         return contracts
  39 
  40     def from_spider(self, spider, results):
  41         requests = []
  42         for method in self.tested_methods_from_spidercls(type(spider)):
  43             bound_method = spider.__getattribute__(method)
  44             requests.append(self.from_method(bound_method, results))
  45 
  46         return requests
  47 
  48     def from_method(self, method, results):
  49         contracts = self.extract_contracts(method)
  50         if contracts:
  51             # calculate request args
  52             args, kwargs = get_spec(Request.__init__)
  53             kwargs['callback'] = method
  54             for contract in contracts:
  55                 kwargs = contract.adjust_request_args(kwargs)
  56 
  57             # create and prepare request
  58             args.remove('self')
  59             if set(args).issubset(set(kwargs)):
  60                 request = Request(**kwargs)
  61 
  62                 # execute pre and post hooks in order
  63                 for contract in reversed(contracts):
  64                     request = contract.add_pre_hook(request, results)
  65                 for contract in contracts:
  66                     request = contract.add_post_hook(request, results)
  67 
  68                 self._clean_req(request, method, results)
  69                 return request
  70 
  71     def _clean_req(self, request, method, results):
  72         """ stop the request from returning objects and records any errors """
  73 
  74         cb = request.callback
  75 
  76         @wraps(cb)
  77         def cb_wrapper(response):
  78             try:
  79                 output = cb(response)
  80                 output = list(iterate_spider_output(output))
  81             except:
  82                 case = _create_testcase(method, 'callback')
  83                 results.addError(case, sys.exc_info())
  84 
  85         def eb_wrapper(failure):
  86             case = _create_testcase(method, 'errback')
  87             exc_info = failure.type, failure.value, failure.getTracebackObject()
  88             results.addError(case, exc_info)
  89 
  90         request.callback = cb_wrapper
  91         request.errback = eb_wrapper
  92 
  93 
  94 class Contract(object):
  95     """ Abstract class for contracts """
  96 
  97     def __init__(self, method, *args):
  98         self.testcase_pre = _create_testcase(method, '@%s pre-hook' % self.name)
  99         self.testcase_post = _create_testcase(method, '@%s post-hook' % self.name)
  100         self.args = args
  101 
  102     def add_pre_hook(self, request, results):
  103         if hasattr(self, 'pre_process'):
  104             cb = request.callback
  105 
  106             @wraps(cb)
  107             def wrapper(response):
  108                 try:
  109                     results.startTest(self.testcase_pre)
  110                     self.pre_process(response)
  111                     results.stopTest(self.testcase_pre)
  112                 except AssertionError:
  113                     results.addFailure(self.testcase_pre, sys.exc_info())
  114                 except Exception:
  115                     results.addError(self.testcase_pre, sys.exc_info())
  116                 else:
  117                     results.addSuccess(self.testcase_pre)
  118                 finally:
  119                     return list(iterate_spider_output(cb(response)))
  120 
  121             request.callback = wrapper
  122 
  123         return request
  124 
  125     def add_post_hook(self, request, results):
  126         if hasattr(self, 'post_process'):
  127             cb = request.callback
  128 
  129             @wraps(cb)
  130             def wrapper(response):
  131                 output = list(iterate_spider_output(cb(response)))
  132                 try:
  133                     results.startTest(self.testcase_post)
  134                     self.post_process(output)
  135                     results.stopTest(self.testcase_post)
  136                 except AssertionError:
  137                     results.addFailure(self.testcase_post, sys.exc_info())
  138                 except Exception:
  139                     results.addError(self.testcase_post, sys.exc_info())
  140                 else:
  141                     results.addSuccess(self.testcase_post)
  142                 finally:
  143                     return output
  144 
  145             request.callback = wrapper
  146 
  147         return request
  148 
  149     def adjust_request_args(self, args):
  150         return args
  151 
  152 
  153 def _create_testcase(method, desc):
  154     spider = method.__self__.name
  155 
  156     class ContractTestCase(TestCase):
  157         def __str__(_self):
  158             return "[%s] %s (%s)" % (spider, method.__name__, desc)
  159 
  160     name = '%s_%s' % (spider, method.__name__)
  161     setattr(ContractTestCase, name, lambda x: x)
  162     return ContractTestCase(name)
  163
  ```
  --- 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/contracts/__init__.py b/scrapy/contracts/__init__.py
  --- a/scrapy/contracts/__init__.py
  +++ b/scrapy/contracts/__init__.py
  @@ -1,6 +1,7 @@
   import sys
   import re
   from functools import wraps
  +from inspect import getmembers
   from unittest import TestCase
   
   from scrapy.http import Request
  @@ -17,7 +18,7 @@
   
       def tested_methods_from_spidercls(self, spidercls):
           methods = []
  -        for key, value in vars(spidercls).items():
  +        for key, value in getmembers(spidercls):
               if (callable(value) and value.__doc__ and
                       re.search(r'^\s*@', value.__doc__, re.MULTILINE)):
                   methods.append(key)
  
verification_info: {"golden_diff": "diff --git a/scrapy/contracts/__init__.py b/scrapy/contracts/__init__.py\n--- a/scrapy/contracts/__init__.py\n+++ b/scrapy/contracts/__init__.py\n@@ -1,6 +1,7 @@\n import sys\n import re\n from functools import wraps\n+from inspect import getmembers\n from unittest import TestCase\n \n from scrapy.http import Request\n@@ -17,7 +18,7 @@\n \n     def tested_methods_from_spidercls(self, spidercls):\n         methods = []\n-        for key, value in vars(spidercls).items():\n+        for key, value in getmembers(spidercls):\n             if (callable(value) and value.__doc__ and\n                     re.search(r'^\\s*@', value.__doc__, re.MULTILINE)):\n                 methods.append(key)\n", "issue": "Cannot use contracts with inherited callbacks\n### Description\r\n\r\nIf you want to `scrapy check` a spider that has inherited methods, these methods' contracts will be ignored.\r\n\r\n### Reproduce\r\n\r\n```python\r\nclass BaseSpider(Spider):\r\n\r\n    def returns_request(self, response):\r\n        \"\"\" method which returns request\r\n        @url https://docs.scrapy.org/en/latest/\r\n        @returns requests 1\r\n        \"\"\"\r\n        return Request('http://scrapy.org', callback=self.returns_item)\r\n\r\n\r\nclass DemoSpider(BaseSpider):\r\n    name = 'demo_spider'\r\n```\r\n\r\nAnd then run `scrapy check`.\r\n\r\nYou'll get the following output:\r\n\r\n```\r\n----------------------------------------------------------------------\r\nRan 0 contracts in 0.000s\r\n\r\nOK\r\n```\r\n\r\n### Reason\r\n\r\n`ContractsManager.tested_methods_from_spidercls` uses `vars(spidercls).items()` to get methods.\r\n\r\n### Solution\r\n\r\nUse `inspect.getmembers(spidercls)` instead.\n", "before_files": [{"content": "import sys\nimport re\nfrom functools import wraps\nfrom unittest import TestCase\n\nfrom scrapy.http import Request\nfrom scrapy.utils.spider import iterate_spider_output\nfrom scrapy.utils.python import get_spec\n\n\nclass ContractsManager(object):\n    contracts = {}\n\n    def __init__(self, contracts):\n        for contract in contracts:\n            self.contracts[contract.name] = contract\n\n    def tested_methods_from_spidercls(self, spidercls):\n        methods = []\n        for key, value in vars(spidercls).items():\n            if (callable(value) and value.__doc__ and\n                    re.search(r'^\\s*@', value.__doc__, re.MULTILINE)):\n                methods.append(key)\n\n        return methods\n\n    def extract_contracts(self, method):\n        contracts = []\n        for line in method.__doc__.split('\\n'):\n            line = line.strip()\n\n            if line.startswith('@'):\n                name, args = re.match(r'@(\\w+)\\s*(.*)', line).groups()\n                args = re.split(r'\\s+', args)\n\n                contracts.append(self.contracts[name](method, *args))\n\n        return contracts\n\n    def from_spider(self, spider, results):\n        requests = []\n        for method in self.tested_methods_from_spidercls(type(spider)):\n            bound_method = spider.__getattribute__(method)\n            requests.append(self.from_method(bound_method, results))\n\n        return requests\n\n    def from_method(self, method, results):\n        contracts = self.extract_contracts(method)\n        if contracts:\n            # calculate request args\n            args, kwargs = get_spec(Request.__init__)\n            kwargs['callback'] = method\n            for contract in contracts:\n                kwargs = contract.adjust_request_args(kwargs)\n\n            # create and prepare request\n            args.remove('self')\n            if set(args).issubset(set(kwargs)):\n                request = Request(**kwargs)\n\n                # execute pre and post hooks in order\n                for contract in reversed(contracts):\n                    request = contract.add_pre_hook(request, results)\n                for contract in contracts:\n                    request = contract.add_post_hook(request, results)\n\n                self._clean_req(request, method, results)\n                return request\n\n    def _clean_req(self, request, method, results):\n        \"\"\" stop the request from returning objects and records any errors \"\"\"\n\n        cb = request.callback\n\n        @wraps(cb)\n        def cb_wrapper(response):\n            try:\n                output = cb(response)\n                output = list(iterate_spider_output(output))\n            except:\n                case = _create_testcase(method, 'callback')\n                results.addError(case, sys.exc_info())\n\n        def eb_wrapper(failure):\n            case = _create_testcase(method, 'errback')\n            exc_info = failure.type, failure.value, failure.getTracebackObject()\n            results.addError(case, exc_info)\n\n        request.callback = cb_wrapper\n        request.errback = eb_wrapper\n\n\nclass Contract(object):\n    \"\"\" Abstract class for contracts \"\"\"\n\n    def __init__(self, method, *args):\n        self.testcase_pre = _create_testcase(method, '@%s pre-hook' % self.name)\n        self.testcase_post = _create_testcase(method, '@%s post-hook' % self.name)\n        self.args = args\n\n    def add_pre_hook(self, request, results):\n        if hasattr(self, 'pre_process'):\n            cb = request.callback\n\n            @wraps(cb)\n            def wrapper(response):\n                try:\n                    results.startTest(self.testcase_pre)\n                    self.pre_process(response)\n                    results.stopTest(self.testcase_pre)\n                except AssertionError:\n                    results.addFailure(self.testcase_pre, sys.exc_info())\n                except Exception:\n                    results.addError(self.testcase_pre, sys.exc_info())\n                else:\n                    results.addSuccess(self.testcase_pre)\n                finally:\n                    return list(iterate_spider_output(cb(response)))\n\n            request.callback = wrapper\n\n        return request\n\n    def add_post_hook(self, request, results):\n        if hasattr(self, 'post_process'):\n            cb = request.callback\n\n            @wraps(cb)\n            def wrapper(response):\n                output = list(iterate_spider_output(cb(response)))\n                try:\n                    results.startTest(self.testcase_post)\n                    self.post_process(output)\n                    results.stopTest(self.testcase_post)\n                except AssertionError:\n                    results.addFailure(self.testcase_post, sys.exc_info())\n                except Exception:\n                    results.addError(self.testcase_post, sys.exc_info())\n                else:\n                    results.addSuccess(self.testcase_post)\n                finally:\n                    return output\n\n            request.callback = wrapper\n\n        return request\n\n    def adjust_request_args(self, args):\n        return args\n\n\ndef _create_testcase(method, desc):\n    spider = method.__self__.name\n\n    class ContractTestCase(TestCase):\n        def __str__(_self):\n            return \"[%s] %s (%s)\" % (spider, method.__name__, desc)\n\n    name = '%s_%s' % (spider, method.__name__)\n    setattr(ContractTestCase, name, lambda x: x)\n    return ContractTestCase(name)\n", "path": "scrapy/contracts/__init__.py"}], "after_files": [{"content": "import sys\nimport re\nfrom functools import wraps\nfrom inspect import getmembers\nfrom unittest import TestCase\n\nfrom scrapy.http import Request\nfrom scrapy.utils.spider import iterate_spider_output\nfrom scrapy.utils.python import get_spec\n\n\nclass ContractsManager(object):\n    contracts = {}\n\n    def __init__(self, contracts):\n        for contract in contracts:\n            self.contracts[contract.name] = contract\n\n    def tested_methods_from_spidercls(self, spidercls):\n        methods = []\n        for key, value in getmembers(spidercls):\n            if (callable(value) and value.__doc__ and\n                    re.search(r'^\\s*@', value.__doc__, re.MULTILINE)):\n                methods.append(key)\n\n        return methods\n\n    def extract_contracts(self, method):\n        contracts = []\n        for line in method.__doc__.split('\\n'):\n            line = line.strip()\n\n            if line.startswith('@'):\n                name, args = re.match(r'@(\\w+)\\s*(.*)', line).groups()\n                args = re.split(r'\\s+', args)\n\n                contracts.append(self.contracts[name](method, *args))\n\n        return contracts\n\n    def from_spider(self, spider, results):\n        requests = []\n        for method in self.tested_methods_from_spidercls(type(spider)):\n            bound_method = spider.__getattribute__(method)\n            requests.append(self.from_method(bound_method, results))\n\n        return requests\n\n    def from_method(self, method, results):\n        contracts = self.extract_contracts(method)\n        if contracts:\n            # calculate request args\n            args, kwargs = get_spec(Request.__init__)\n            kwargs['callback'] = method\n            for contract in contracts:\n                kwargs = contract.adjust_request_args(kwargs)\n\n            # create and prepare request\n            args.remove('self')\n            if set(args).issubset(set(kwargs)):\n                request = Request(**kwargs)\n\n                # execute pre and post hooks in order\n                for contract in reversed(contracts):\n                    request = contract.add_pre_hook(request, results)\n                for contract in contracts:\n                    request = contract.add_post_hook(request, results)\n\n                self._clean_req(request, method, results)\n                return request\n\n    def _clean_req(self, request, method, results):\n        \"\"\" stop the request from returning objects and records any errors \"\"\"\n\n        cb = request.callback\n\n        @wraps(cb)\n        def cb_wrapper(response):\n            try:\n                output = cb(response)\n                output = list(iterate_spider_output(output))\n            except:\n                case = _create_testcase(method, 'callback')\n                results.addError(case, sys.exc_info())\n\n        def eb_wrapper(failure):\n            case = _create_testcase(method, 'errback')\n            exc_info = failure.type, failure.value, failure.getTracebackObject()\n            results.addError(case, exc_info)\n\n        request.callback = cb_wrapper\n        request.errback = eb_wrapper\n\n\nclass Contract(object):\n    \"\"\" Abstract class for contracts \"\"\"\n\n    def __init__(self, method, *args):\n        self.testcase_pre = _create_testcase(method, '@%s pre-hook' % self.name)\n        self.testcase_post = _create_testcase(method, '@%s post-hook' % self.name)\n        self.args = args\n\n    def add_pre_hook(self, request, results):\n        if hasattr(self, 'pre_process'):\n            cb = request.callback\n\n            @wraps(cb)\n            def wrapper(response):\n                try:\n                    results.startTest(self.testcase_pre)\n                    self.pre_process(response)\n                    results.stopTest(self.testcase_pre)\n                except AssertionError:\n                    results.addFailure(self.testcase_pre, sys.exc_info())\n                except Exception:\n                    results.addError(self.testcase_pre, sys.exc_info())\n                else:\n                    results.addSuccess(self.testcase_pre)\n                finally:\n                    return list(iterate_spider_output(cb(response)))\n\n            request.callback = wrapper\n\n        return request\n\n    def add_post_hook(self, request, results):\n        if hasattr(self, 'post_process'):\n            cb = request.callback\n\n            @wraps(cb)\n            def wrapper(response):\n                output = list(iterate_spider_output(cb(response)))\n                try:\n                    results.startTest(self.testcase_post)\n                    self.post_process(output)\n                    results.stopTest(self.testcase_post)\n                except AssertionError:\n                    results.addFailure(self.testcase_post, sys.exc_info())\n                except Exception:\n                    results.addError(self.testcase_post, sys.exc_info())\n                else:\n                    results.addSuccess(self.testcase_post)\n                finally:\n                    return output\n\n            request.callback = wrapper\n\n        return request\n\n    def adjust_request_args(self, args):\n        return args\n\n\ndef _create_testcase(method, desc):\n    spider = method.__self__.name\n\n    class ContractTestCase(TestCase):\n        def __str__(_self):\n            return \"[%s] %s (%s)\" % (spider, method.__name__, desc)\n\n    name = '%s_%s' % (spider, method.__name__)\n    setattr(ContractTestCase, name, lambda x: x)\n    return ContractTestCase(name)\n", "path": "scrapy/contracts/__init__.py"}]}