diff --git a/docs/news.rst b/docs/news.rst index 8c94659b..5b1f6678 100644 --- a/docs/news.rst +++ b/docs/news.rst @@ -26,7 +26,11 @@ API ^^^ - If the ``egg`` parameter to the :ref:`addversion.json`` webservice is not a ZIP file, use the error message, "egg is not a ZIP file (if using curl, use egg=@path not egg=path)". -- Clarify some error messages: for example, ``'project' parameter is required`` instead of ``'project'``, and ``exception class: message`` instead of ``message``. +- Clarify some error messages, for example: + + - ``'project' parameter is required`` instead of ``'project'`` + - ``exception class: message`` instead of ``message`` + - ``ValueError: Unknown or corrupt egg`` instead of ``TypeError: 'tuple' object is not an iterator`` CLI ^^^ diff --git a/scrapyd/eggutils.py b/scrapyd/eggutils.py index 92f99182..87ef6bc2 100644 --- a/scrapyd/eggutils.py +++ b/scrapyd/eggutils.py @@ -8,8 +8,11 @@ def activate_egg(eggpath): to activate a Scrapy egg file. Don't use it from other code as it may leave unwanted side effects. """ + distributions = pkg_resources.find_distributions(eggpath) + if isinstance(distributions, tuple): + raise ValueError("Unknown or corrupt egg") try: - d = next(pkg_resources.find_distributions(eggpath)) + d = next(distributions) except StopIteration: raise ValueError("Unknown or corrupt egg") d.activate() diff --git a/scrapyd/exceptions.py b/scrapyd/exceptions.py index f75b5045..5db6ab49 100644 --- a/scrapyd/exceptions.py +++ b/scrapyd/exceptions.py @@ -4,3 +4,7 @@ class ScrapydError(Exception): class MissingRequiredArgument(ScrapydError): """Raised if a required argument is missing""" + + +class RunnerError(ScrapydError): + """Raised if the runner returns an error code""" diff --git a/scrapyd/tests/test_utils.py b/scrapyd/tests/test_utils.py index aa8b5d46..41a80267 100644 --- a/scrapyd/tests/test_utils.py +++ b/scrapyd/tests/test_utils.py @@ -10,6 +10,7 @@ from twisted.trial import unittest from scrapyd import get_application +from scrapyd.exceptions import RunnerError from scrapyd.interfaces import IEggStorage from scrapyd.utils import UtilsCache, get_crawl_args, get_spider_list, sorted_versions @@ -123,7 +124,7 @@ def popen_wrapper(*args, **kwargs): return Popen(cmd, *args, **kwargs) with mock.patch('scrapyd.utils.Popen', wraps=popen_wrapper): - exc = self.assertRaises(RuntimeError, get_spider_list, 'mybot3', pythonpath=pypath) + exc = self.assertRaises(RunnerError, get_spider_list, 'mybot3', pythonpath=pypath) self.assertRegex(str(exc).rstrip(), r'Exception: This should break the `scrapy list` command$') diff --git a/scrapyd/utils.py b/scrapyd/utils.py index baf26fc5..2002a7bf 100644 --- a/scrapyd/utils.py +++ b/scrapyd/utils.py @@ -9,6 +9,7 @@ from twisted.web import resource from scrapyd.config import Config +from scrapyd.exceptions import RunnerError from scrapyd.sqlite import JsonSqliteDict @@ -139,7 +140,7 @@ def get_spider_list(project, runner=None, pythonpath=None, version=''): if proc.returncode: msg = err or out or '' msg = msg.decode('utf8') - raise RuntimeError(msg) + raise RunnerError(msg) # FIXME: can we reliably decode as UTF-8? # scrapy list does `print(list)` tmp = out.decode('utf-8').splitlines() diff --git a/scrapyd/webservice.py b/scrapyd/webservice.py index 09da9918..5f059da7 100644 --- a/scrapyd/webservice.py +++ b/scrapyd/webservice.py @@ -118,9 +118,10 @@ def render_POST(self, txrequest): class AddVersion(WsResource): def render_POST(self, txrequest): - eggf = BytesIO(_pop_required_param(txrequest.args, b'egg')[0]) - if not zipfile.is_zipfile(eggf): + egg = _pop_required_param(txrequest.args, b'egg')[0] + if not zipfile.is_zipfile(BytesIO(egg)): return {"status": "error", "message": "egg is not a ZIP file (if using curl, use egg=@path not egg=path)"} + eggf = BytesIO(egg) args = native_stringify_dict(copy(txrequest.args), keys_only=False) project = _get_required_param(args, 'project')[0] version = _get_required_param(args, 'version')[0]