1
0
mirror of https://github.com/scrapy/scrapy.git synced 2025-02-06 22:51:39 +00:00

Update CI to support Twisted 21.2.0 (#5027)

This commit is contained in:
Adrián Chaves 2021-03-19 18:39:44 +01:00 committed by GitHub
parent 0dad0fce72
commit 308a58aa27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 53 additions and 13 deletions

View File

@ -9,9 +9,8 @@ from contextlib import suppress
from io import BytesIO from io import BytesIO
from itemadapter import ItemAdapter from itemadapter import ItemAdapter
from PIL import Image
from scrapy.exceptions import DropItem from scrapy.exceptions import DropItem, NotConfigured
from scrapy.http import Request from scrapy.http import Request
from scrapy.pipelines.files import FileException, FilesPipeline from scrapy.pipelines.files import FileException, FilesPipeline
# TODO: from scrapy.pipelines.media import MediaPipeline # TODO: from scrapy.pipelines.media import MediaPipeline
@ -45,6 +44,14 @@ class ImagesPipeline(FilesPipeline):
DEFAULT_IMAGES_RESULT_FIELD = 'images' DEFAULT_IMAGES_RESULT_FIELD = 'images'
def __init__(self, store_uri, download_func=None, settings=None): def __init__(self, store_uri, download_func=None, settings=None):
try:
from PIL import Image
self._Image = Image
except ImportError:
raise NotConfigured(
'ImagesPipeline requires installing Pillow 4.0.0 or later'
)
super().__init__(store_uri, settings=settings, download_func=download_func) super().__init__(store_uri, settings=settings, download_func=download_func)
if isinstance(settings, dict) or settings is None: if isinstance(settings, dict) or settings is None:
@ -121,7 +128,7 @@ class ImagesPipeline(FilesPipeline):
def get_images(self, response, request, info, *, item=None): def get_images(self, response, request, info, *, item=None):
path = self.file_path(request, response=response, info=info, item=item) path = self.file_path(request, response=response, info=info, item=item)
orig_image = Image.open(BytesIO(response.body)) orig_image = self._Image.open(BytesIO(response.body))
width, height = orig_image.size width, height = orig_image.size
if width < self.min_width or height < self.min_height: if width < self.min_width or height < self.min_height:
@ -139,12 +146,12 @@ class ImagesPipeline(FilesPipeline):
def convert_image(self, image, size=None): def convert_image(self, image, size=None):
if image.format == 'PNG' and image.mode == 'RGBA': if image.format == 'PNG' and image.mode == 'RGBA':
background = Image.new('RGBA', image.size, (255, 255, 255)) background = self._Image.new('RGBA', image.size, (255, 255, 255))
background.paste(image, image) background.paste(image, image)
image = background.convert('RGB') image = background.convert('RGB')
elif image.mode == 'P': elif image.mode == 'P':
image = image.convert("RGBA") image = image.convert("RGBA")
background = Image.new('RGBA', image.size, (255, 255, 255)) background = self._Image.new('RGBA', image.size, (255, 255, 255))
background.paste(image, image) background.paste(image, image)
image = background.convert('RGB') image = background.convert('RGB')
elif image.mode != 'RGB': elif image.mode != 'RGB':
@ -152,7 +159,7 @@ class ImagesPipeline(FilesPipeline):
if size: if size:
image = image.copy() image = image.copy()
image.thumbnail(size, Image.ANTIALIAS) image.thumbnail(size, self._Image.ANTIALIAS)
buf = BytesIO() buf = BytesIO()
image.save(buf, 'JPEG') image.save(buf, 'JPEG')

View File

@ -17,6 +17,8 @@ from threading import Timer
from unittest import skipIf from unittest import skipIf
from pytest import mark from pytest import mark
from twisted import version as twisted_version
from twisted.python.versions import Version
from twisted.trial import unittest from twisted.trial import unittest
import scrapy import scrapy
@ -630,6 +632,7 @@ class MySpider(scrapy.Spider):
@mark.skipif(sys.implementation.name == 'pypy', reason='uvloop does not support pypy properly') @mark.skipif(sys.implementation.name == 'pypy', reason='uvloop does not support pypy properly')
@mark.skipif(platform.system() == 'Windows', reason='uvloop does not support Windows') @mark.skipif(platform.system() == 'Windows', reason='uvloop does not support Windows')
@mark.skipif(twisted_version == Version('twisted', 21, 2, 0), reason='https://twistedmatrix.com/trac/ticket/10106')
def test_custom_asyncio_loop_enabled_true(self): def test_custom_asyncio_loop_enabled_true(self):
log = self.get_log(self.debug_log_spider, args=[ log = self.get_log(self.debug_log_spider, args=[
'-s', '-s',

View File

@ -8,7 +8,9 @@ from unittest import skipIf
from pytest import raises, mark from pytest import raises, mark
from testfixtures import LogCapture from testfixtures import LogCapture
from twisted import version as twisted_version
from twisted.internet import defer from twisted.internet import defer
from twisted.python.versions import Version
from twisted.trial import unittest from twisted.trial import unittest
import scrapy import scrapy
@ -358,6 +360,7 @@ class CrawlerProcessSubprocess(ScriptRunnerMixin, unittest.TestCase):
@mark.skipif(sys.implementation.name == 'pypy', reason='uvloop does not support pypy properly') @mark.skipif(sys.implementation.name == 'pypy', reason='uvloop does not support pypy properly')
@mark.skipif(platform.system() == 'Windows', reason='uvloop does not support Windows') @mark.skipif(platform.system() == 'Windows', reason='uvloop does not support Windows')
@mark.skipif(twisted_version == Version('twisted', 21, 2, 0), reason='https://twistedmatrix.com/trac/ticket/10106')
def test_custom_loop_asyncio(self): def test_custom_loop_asyncio(self):
log = self.run_script("asyncio_custom_loop.py") log = self.run_script("asyncio_custom_loop.py")
self.assertIn("Spider closed (finished)", log) self.assertIn("Spider closed (finished)", log)
@ -366,6 +369,7 @@ class CrawlerProcessSubprocess(ScriptRunnerMixin, unittest.TestCase):
@mark.skipif(sys.implementation.name == "pypy", reason="uvloop does not support pypy properly") @mark.skipif(sys.implementation.name == "pypy", reason="uvloop does not support pypy properly")
@mark.skipif(platform.system() == "Windows", reason="uvloop does not support Windows") @mark.skipif(platform.system() == "Windows", reason="uvloop does not support Windows")
@mark.skipif(twisted_version == Version('twisted', 21, 2, 0), reason='https://twistedmatrix.com/trac/ticket/10106')
def test_custom_loop_asyncio_deferred_signal(self): def test_custom_loop_asyncio_deferred_signal(self):
log = self.run_script("asyncio_deferred_signal.py", "uvloop.Loop") log = self.run_script("asyncio_deferred_signal.py", "uvloop.Loop")
self.assertIn("Spider closed (finished)", log) self.assertIn("Spider closed (finished)", log)

View File

@ -180,7 +180,18 @@ class FileDownloadCrawlTestCase(TestCase):
self.assertEqual(crawler.stats.get_value('downloader/response_status_count/302'), 3) self.assertEqual(crawler.stats.get_value('downloader/response_status_count/302'), 3)
try:
from PIL import Image # noqa: imported just to check for the import error
except ImportError:
skip_pillow = 'Missing Python Imaging Library, install https://pypi.python.org/pypi/Pillow'
else:
skip_pillow = None
class ImageDownloadCrawlTestCase(FileDownloadCrawlTestCase): class ImageDownloadCrawlTestCase(FileDownloadCrawlTestCase):
skip = skip_pillow
pipeline_class = 'scrapy.pipelines.images.ImagesPipeline' pipeline_class = 'scrapy.pipelines.images.ImagesPipeline'
store_setting_key = 'IMAGES_STORE' store_setting_key = 'IMAGES_STORE'
media_key = 'images' media_key = 'images'

View File

@ -23,15 +23,16 @@ except ImportError:
dataclass_field = None dataclass_field = None
skip = False
try: try:
from PIL import Image from PIL import Image
except ImportError: except ImportError:
skip = 'Missing Python Imaging Library, install https://pypi.python.org/pypi/Pillow' skip_pillow = 'Missing Python Imaging Library, install https://pypi.python.org/pypi/Pillow'
else: else:
encoders = {'jpeg_encoder', 'jpeg_decoder'} encoders = {'jpeg_encoder', 'jpeg_decoder'}
if not encoders.issubset(set(Image.core.__dict__)): if not encoders.issubset(set(Image.core.__dict__)):
skip = 'Missing JPEG encoders' skip_pillow = 'Missing JPEG encoders'
else:
skip_pillow = None
def _mocked_download_func(request, info): def _mocked_download_func(request, info):
@ -41,7 +42,7 @@ def _mocked_download_func(request, info):
class ImagesPipelineTestCase(unittest.TestCase): class ImagesPipelineTestCase(unittest.TestCase):
skip = skip skip = skip_pillow
def setUp(self): def setUp(self):
self.tempdir = mkdtemp() self.tempdir = mkdtemp()
@ -137,6 +138,8 @@ class DeprecatedImagesPipeline(ImagesPipeline):
class ImagesPipelineTestCaseFieldsMixin: class ImagesPipelineTestCaseFieldsMixin:
skip = skip_pillow
def test_item_fields_default(self): def test_item_fields_default(self):
url = 'http://www.example.com/images/1.jpg' url = 'http://www.example.com/images/1.jpg'
item = self.item_class(name='item1', image_urls=[url]) item = self.item_class(name='item1', image_urls=[url])
@ -221,6 +224,9 @@ class ImagesPipelineTestCaseFieldsAttrsItem(ImagesPipelineTestCaseFieldsMixin, u
class ImagesPipelineTestCaseCustomSettings(unittest.TestCase): class ImagesPipelineTestCaseCustomSettings(unittest.TestCase):
skip = skip_pillow
img_cls_attribute_names = [ img_cls_attribute_names = [
# Pipeline attribute names with corresponding setting names. # Pipeline attribute names with corresponding setting names.
("EXPIRES", "IMAGES_EXPIRES"), ("EXPIRES", "IMAGES_EXPIRES"),

View File

@ -1,3 +1,5 @@
from typing import Optional
from testfixtures import LogCapture from testfixtures import LogCapture
from twisted.trial import unittest from twisted.trial import unittest
from twisted.python.failure import Failure from twisted.python.failure import Failure
@ -17,6 +19,14 @@ from scrapy.utils.signal import disconnect_all
from scrapy import signals from scrapy import signals
try:
from PIL import Image # noqa: imported just to check for the import error
except ImportError:
skip_pillow: Optional[str] = 'Missing Python Imaging Library, install https://pypi.python.org/pypi/Pillow'
else:
skip_pillow = None
def _mocked_download_func(request, info): def _mocked_download_func(request, info):
response = request.meta.get('response') response = request.meta.get('response')
return response() if callable(response) else response return response() if callable(response) else response
@ -379,6 +389,7 @@ class MockedMediaPipelineDeprecatedMethods(ImagesPipeline):
class MediaPipelineDeprecatedMethodsTestCase(unittest.TestCase): class MediaPipelineDeprecatedMethodsTestCase(unittest.TestCase):
skip = skip_pillow
def setUp(self): def setUp(self):
self.pipe = MockedMediaPipelineDeprecatedMethods(store_uri='store-uri', download_func=_mocked_download_func) self.pipe = MockedMediaPipelineDeprecatedMethods(store_uri='store-uri', download_func=_mocked_download_func)

View File

@ -19,9 +19,6 @@ deps =
mitmproxy >= 4.0.4, < 5; python_version >= '3.6' and python_version < '3.7' and platform_system != 'Windows' and implementation_name != 'pypy' mitmproxy >= 4.0.4, < 5; python_version >= '3.6' and python_version < '3.7' and platform_system != 'Windows' and implementation_name != 'pypy'
# Extras # Extras
botocore>=1.4.87 botocore>=1.4.87
Pillow>=4.0.0
# Twisted 21+ causes issues in tests that use skipIf
Twisted[http2]>=17.9.0,<21
passenv = passenv =
S3_TEST_FILE_URI S3_TEST_FILE_URI
AWS_ACCESS_KEY_ID AWS_ACCESS_KEY_ID
@ -124,6 +121,7 @@ deps =
{[testenv]deps} {[testenv]deps}
reppy reppy
robotexclusionrulesparser robotexclusionrulesparser
Pillow>=4.0.0
[testenv:asyncio] [testenv:asyncio]
commands = commands =