mirror of https://github.com/scrapy/scrapy.git
Merge pull request #733 from alexcepoi/contracts_tweaks
improvements to scrapy check/contracts
This commit is contained in:
commit
791889f51d
|
|
@ -1,22 +1,42 @@
|
|||
from __future__ import print_function
|
||||
import time
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from functools import wraps
|
||||
from unittest import TextTestRunner
|
||||
from unittest import TextTestRunner, TextTestResult as _TextTestResult
|
||||
|
||||
from scrapy.command import ScrapyCommand
|
||||
from scrapy.contracts import ContractsManager
|
||||
from scrapy.utils.misc import load_object
|
||||
from scrapy.utils.spider import iterate_spider_output
|
||||
from scrapy.utils.conf import build_component_list
|
||||
|
||||
|
||||
def _generate(cb):
|
||||
""" create a callback which does not return anything """
|
||||
@wraps(cb)
|
||||
def wrapper(response):
|
||||
output = cb(response)
|
||||
output = list(iterate_spider_output(output))
|
||||
return wrapper
|
||||
class TextTestResult(_TextTestResult):
|
||||
def printSummary(self, start, stop):
|
||||
write = self.stream.write
|
||||
writeln = self.stream.writeln
|
||||
|
||||
run = self.testsRun
|
||||
plural = "s" if run != 1 else ""
|
||||
|
||||
writeln(self.separator2)
|
||||
writeln("Ran %d contract%s in %.3fs" % (run, plural, stop - start))
|
||||
writeln()
|
||||
|
||||
infos = []
|
||||
if not self.wasSuccessful():
|
||||
write("FAILED")
|
||||
failed, errored = map(len, (self.failures, self.errors))
|
||||
if failed:
|
||||
infos.append("failures=%d" % failed)
|
||||
if errored:
|
||||
infos.append("errors=%d" % errored)
|
||||
else:
|
||||
write("OK")
|
||||
|
||||
if infos:
|
||||
writeln(" (%s)" % (", ".join(infos),))
|
||||
else:
|
||||
write("\n")
|
||||
|
||||
|
||||
class Command(ScrapyCommand):
|
||||
|
|
@ -32,9 +52,9 @@ class Command(ScrapyCommand):
|
|||
def add_options(self, parser):
|
||||
ScrapyCommand.add_options(self, parser)
|
||||
parser.add_option("-l", "--list", dest="list", action="store_true",
|
||||
help="only list contracts, without checking them")
|
||||
help="only list contracts, without checking them")
|
||||
parser.add_option("-v", "--verbose", dest="verbose", default=1, action="count",
|
||||
help="print all contract hooks")
|
||||
help="print all contract hooks")
|
||||
|
||||
def run(self, args, opts):
|
||||
# load contracts
|
||||
|
|
@ -42,8 +62,9 @@ class Command(ScrapyCommand):
|
|||
self.settings['SPIDER_CONTRACTS_BASE'],
|
||||
self.settings['SPIDER_CONTRACTS'],
|
||||
)
|
||||
self.conman = ContractsManager([load_object(c) for c in contracts])
|
||||
self.results = TextTestRunner(verbosity=opts.verbose)._makeResult()
|
||||
conman = ContractsManager([load_object(c) for c in contracts])
|
||||
runner = TextTestRunner(verbosity=opts.verbose)
|
||||
result = TextTestResult(runner.stream, runner.descriptions, runner.verbosity)
|
||||
|
||||
# contract requests
|
||||
contract_reqs = defaultdict(list)
|
||||
|
|
@ -53,7 +74,7 @@ class Command(ScrapyCommand):
|
|||
|
||||
for spider in args or spiders.list():
|
||||
spider = spiders.create(spider)
|
||||
requests = self.get_requests(spider)
|
||||
requests = self.get_requests(spider, conman, result)
|
||||
|
||||
if opts.list:
|
||||
for req in requests:
|
||||
|
|
@ -69,20 +90,23 @@ class Command(ScrapyCommand):
|
|||
for method in sorted(methods):
|
||||
print(' * %s' % method)
|
||||
else:
|
||||
start = time.time()
|
||||
self.crawler_process.start()
|
||||
self.results.printErrors()
|
||||
self.exitcode = 0 if self.results.wasSuccessful() else 1
|
||||
stop = time.time()
|
||||
|
||||
def get_requests(self, spider):
|
||||
result.printErrors()
|
||||
result.printSummary(start, stop)
|
||||
self.exitcode = int(not result.wasSuccessful())
|
||||
|
||||
def get_requests(self, spider, conman, result):
|
||||
requests = []
|
||||
|
||||
for key, value in vars(type(spider)).items():
|
||||
if callable(value) and value.__doc__:
|
||||
bound_method = value.__get__(spider, type(spider))
|
||||
request = self.conman.from_method(bound_method, self.results)
|
||||
request = conman.from_method(bound_method, result)
|
||||
|
||||
if request:
|
||||
request.callback = _generate(request.callback)
|
||||
requests.append(request)
|
||||
|
||||
return requests
|
||||
|
|
|
|||
|
|
@ -48,28 +48,40 @@ class ContractsManager(object):
|
|||
for contract in contracts:
|
||||
request = contract.add_post_hook(request, results)
|
||||
|
||||
self._clean_req(request, method, results)
|
||||
return request
|
||||
|
||||
def _clean_req(self, request, method, results):
|
||||
""" stop the request from returning objects and records any errors """
|
||||
|
||||
cb = request.callback
|
||||
|
||||
@wraps(cb)
|
||||
def cb_wrapper(response):
|
||||
try:
|
||||
output = cb(response)
|
||||
output = list(iterate_spider_output(output))
|
||||
except:
|
||||
case = _create_testcase(method, 'callback')
|
||||
results.addError(case, sys.exc_info())
|
||||
|
||||
def eb_wrapper(failure):
|
||||
case = _create_testcase(method, 'errback')
|
||||
exc_info = failure.value, failure.type, failure.getTracebackObject()
|
||||
results.addError(case, exc_info)
|
||||
|
||||
request.callback = cb_wrapper
|
||||
request.errback = eb_wrapper
|
||||
|
||||
|
||||
class Contract(object):
|
||||
""" Abstract class for contracts """
|
||||
|
||||
def __init__(self, method, *args):
|
||||
self.testcase_pre = self.create_testcase(method, 'pre-hook')
|
||||
self.testcase_post = self.create_testcase(method, 'post-hook')
|
||||
self.testcase_pre = _create_testcase(method, '@%s pre-hook' % self.name)
|
||||
self.testcase_post = _create_testcase(method, '@%s post-hook' % self.name)
|
||||
self.args = args
|
||||
|
||||
def create_testcase(self, method, hook):
|
||||
spider = method.__self__.name
|
||||
|
||||
class ContractTestCase(TestCase):
|
||||
def __str__(_self):
|
||||
return "[%s] %s (@%s %s)" % (spider, method.__name__, self.name, hook)
|
||||
|
||||
name = '%s_%s' % (spider, method.__name__)
|
||||
setattr(ContractTestCase, name, lambda x: x)
|
||||
return ContractTestCase(name)
|
||||
|
||||
def add_pre_hook(self, request, results):
|
||||
if hasattr(self, 'pre_process'):
|
||||
cb = request.callback
|
||||
|
|
@ -119,3 +131,15 @@ class Contract(object):
|
|||
|
||||
def adjust_request_args(self, args):
|
||||
return args
|
||||
|
||||
|
||||
def _create_testcase(method, desc):
|
||||
spider = method.__self__.name
|
||||
|
||||
class ContractTestCase(TestCase):
|
||||
def __str__(_self):
|
||||
return "[%s] %s (%s)" % (spider, method.__name__, desc)
|
||||
|
||||
name = '%s_%s' % (spider, method.__name__)
|
||||
setattr(ContractTestCase, name, lambda x: x)
|
||||
return ContractTestCase(name)
|
||||
|
|
|
|||
Loading…
Reference in New Issue