mirror of https://github.com/scrapy/scrapy.git
Return non-zero exit code from scrapy commands in case of spider bootstrap errors
* method to detect spider creation in crawler * correct method name * method to know if crawlers has spiders * we do not need to issue requests * set exit code accordingly to spiders in crawlers * more portable way to check ofr exceptions * more clear way * test cases for several spiders per crawler * grammatically correct name for method * method is private * grammatically correct name for method * method is private * remove unused import * correct order of imports * changes mechanism of obtaining spider status from method to object member * rename tests
This commit is contained in:
parent
667eb7151b
commit
72d0899bce
|
|
@ -56,3 +56,6 @@ class Command(ScrapyCommand):
|
|||
|
||||
self.crawler_process.crawl(spname, **opts.spargs)
|
||||
self.crawler_process.start()
|
||||
|
||||
if self.crawler_process.bootstrap_failed:
|
||||
self.exitcode = 1
|
||||
|
|
|
|||
|
|
@ -87,3 +87,6 @@ class Command(ScrapyCommand):
|
|||
|
||||
self.crawler_process.crawl(spidercls, **opts.spargs)
|
||||
self.crawler_process.start()
|
||||
|
||||
if self.crawler_process.bootstrap_failed:
|
||||
self.exitcode = 1
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ class CrawlerRunner(object):
|
|||
self.spider_loader = _get_spider_loader(settings)
|
||||
self._crawlers = set()
|
||||
self._active = set()
|
||||
self.bootstrap_failed = False
|
||||
|
||||
@property
|
||||
def spiders(self):
|
||||
|
|
@ -178,6 +179,7 @@ class CrawlerRunner(object):
|
|||
def _done(result):
|
||||
self.crawlers.discard(crawler)
|
||||
self._active.discard(d)
|
||||
self.bootstrap_failed |= not getattr(crawler, 'spider', None)
|
||||
return result
|
||||
|
||||
return d.addBoth(_done)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import inspect
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
|
@ -17,6 +18,7 @@ from scrapy.utils.python import retry_on_eintr
|
|||
from scrapy.utils.test import get_testenv
|
||||
from scrapy.utils.testsite import SiteTest
|
||||
from scrapy.utils.testproc import ProcessTest
|
||||
from tests.test_crawler import ExceptionSpider, NoRequestsSpider
|
||||
|
||||
|
||||
class ProjectTest(unittest.TestCase):
|
||||
|
|
@ -220,6 +222,16 @@ class MySpider(scrapy.Spider):
|
|||
self.assertIn("INFO: Closing spider (finished)", log)
|
||||
self.assertIn("INFO: Spider closed (finished)", log)
|
||||
|
||||
def test_run_fail_spider(self):
|
||||
proc = self.runspider("import scrapy\n" + inspect.getsource(ExceptionSpider))
|
||||
ret = proc.returncode
|
||||
self.assertNotEqual(ret, 0)
|
||||
|
||||
def test_run_good_spider(self):
|
||||
proc = self.runspider("import scrapy\n" + inspect.getsource(NoRequestsSpider))
|
||||
ret = proc.returncode
|
||||
self.assertEqual(ret, 0)
|
||||
|
||||
def test_runspider_log_level(self):
|
||||
log = self.get_log(self.debug_log_spider,
|
||||
args=('-s', 'LOG_LEVEL=INFO'))
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@ import tempfile
|
|||
import warnings
|
||||
import unittest
|
||||
|
||||
from twisted.internet import defer
|
||||
import twisted.trial.unittest
|
||||
|
||||
import scrapy
|
||||
from scrapy.crawler import Crawler, CrawlerRunner, CrawlerProcess
|
||||
from scrapy.settings import Settings, default_settings
|
||||
|
|
@ -11,9 +14,9 @@ from scrapy.spiderloader import SpiderLoader
|
|||
from scrapy.utils.log import configure_logging, get_scrapy_root_handler
|
||||
from scrapy.utils.spider import DefaultSpider
|
||||
from scrapy.utils.misc import load_object
|
||||
from scrapy.utils.test import get_crawler
|
||||
from scrapy.extensions.throttle import AutoThrottle
|
||||
|
||||
|
||||
class BaseCrawlerTest(unittest.TestCase):
|
||||
|
||||
def assertOptionIsDefault(self, settings, key):
|
||||
|
|
@ -181,3 +184,62 @@ class CrawlerProcessTest(BaseCrawlerTest):
|
|||
def test_crawler_process_accepts_None(self):
|
||||
runner = CrawlerProcess()
|
||||
self.assertOptionIsDefault(runner.settings, 'RETRY_ENABLED')
|
||||
|
||||
|
||||
class ExceptionSpider(scrapy.Spider):
|
||||
name = 'exception'
|
||||
|
||||
@classmethod
|
||||
def from_crawler(cls, crawler, *args, **kwargs):
|
||||
raise ValueError('Exception in from_crawler method')
|
||||
|
||||
|
||||
class NoRequestsSpider(scrapy.Spider):
|
||||
name = 'no_request'
|
||||
|
||||
def start_requests(self):
|
||||
return []
|
||||
|
||||
|
||||
class CrawlerRunnerHasSpider(twisted.trial.unittest.TestCase):
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_crawler_runner_bootstrap_successful(self):
|
||||
runner = CrawlerRunner()
|
||||
yield runner.crawl(NoRequestsSpider)
|
||||
self.assertEqual(runner.bootstrap_failed, False)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_crawler_runner_bootstrap_successful_for_several(self):
|
||||
runner = CrawlerRunner()
|
||||
yield runner.crawl(NoRequestsSpider)
|
||||
yield runner.crawl(NoRequestsSpider)
|
||||
self.assertEqual(runner.bootstrap_failed, False)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_crawler_runner_bootstrap_failed(self):
|
||||
runner = CrawlerRunner()
|
||||
|
||||
try:
|
||||
yield runner.crawl(ExceptionSpider)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
self.fail('Exception should be raised from spider')
|
||||
|
||||
self.assertEqual(runner.bootstrap_failed, True)
|
||||
|
||||
@defer.inlineCallbacks
|
||||
def test_crawler_runner_bootstrap_failed_for_several(self):
|
||||
runner = CrawlerRunner()
|
||||
|
||||
try:
|
||||
yield runner.crawl(ExceptionSpider)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
self.fail('Exception should be raised from spider')
|
||||
|
||||
yield runner.crawl(NoRequestsSpider)
|
||||
|
||||
self.assertEqual(runner.bootstrap_failed, True)
|
||||
|
|
|
|||
Loading…
Reference in New Issue