Update Python version references after dropping support for 3.5 (#4742)

* Update Python version references after dropping support for 3.5

* Remove outdated test

* Undo change affecting collect_asyncgen

* Undo change to be handled by #4743

* Remove unused import

* Remove unused import

* Update tests/requirements-py3.txt

Co-authored-by: Mikhail Korobov <kmike84@gmail.com>

Co-authored-by: Mikhail Korobov <kmike84@gmail.com>
This commit is contained in:
Adrián Chaves 2020-08-27 12:43:43 +02:00 committed by GitHub
parent a6b67cf4a1
commit 195f738bba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 17 additions and 63 deletions

View File

@ -40,7 +40,7 @@ including a list of features.
Requirements
============
* Python 3.5.2+
* Python 3.6+
* Works on Linux, Windows, macOS, BSD
Install

View File

@ -9,8 +9,8 @@ Installation guide
Supported Python versions
=========================
Scrapy requires Python 3.5.2+, either the CPython implementation (default) or
the PyPy 5.9+ implementation (see :ref:`python:implementations`).
Scrapy requires Python 3.6+, either the CPython implementation (default) or
the PyPy 7.2.0+ implementation (see :ref:`python:implementations`).
Installing Scrapy

View File

@ -405,8 +405,6 @@ to get all of them:
from sys import version_info
.. skip: next if(version_info < (3, 6), reason="Only Python 3.6+ dictionaries match the output")
Having figured out how to extract each bit, we can now iterate over all the
quotes elements and put them together into a Python dictionary:

View File

@ -17,19 +17,14 @@ hence use coroutine syntax (e.g. ``await``, ``async for``, ``async with``):
- :class:`~scrapy.http.Request` callbacks.
The following are known caveats of the current implementation that we aim
to address in future versions of Scrapy:
- The callback output is not processed until the whole callback finishes.
.. note:: The callback output is not processed until the whole callback
finishes.
As a side effect, if the callback raises an exception, none of its
output is processed.
- Because `asynchronous generators were introduced in Python 3.6`_, you
can only use ``yield`` if you are using Python 3.6 or later.
If you need to output multiple items or requests and you are using
Python 3.5, return an iterable (e.g. a list) instead.
This is a known caveat of the current implementation that we aim to
address in a future version of Scrapy.
- The :meth:`process_item` method of
:ref:`item pipelines <topics-item-pipeline>`.
@ -44,8 +39,6 @@ hence use coroutine syntax (e.g. ``await``, ``async for``, ``async with``):
- :ref:`Signal handlers that support deferreds <signal-deferred>`.
.. _asynchronous generators were introduced in Python 3.6: https://www.python.org/dev/peps/pep-0525/
Usage
=====

View File

@ -28,8 +28,8 @@ twisted_version = (_txv.major, _txv.minor, _txv.micro)
# Check minimum required Python version
if sys.version_info < (3, 5, 2):
print("Scrapy %s requires Python 3.5.2" % __version__)
if sys.version_info < (3, 6):
print("Scrapy %s requires Python 3.6+" % __version__)
sys.exit(1)

View File

@ -124,18 +124,11 @@ def iter_errback(iterable, errback, *a, **kw):
errback(failure.Failure(), *a, **kw)
def _isfuture(o):
# workaround for Python before 3.5.3 not having asyncio.isfuture
if hasattr(asyncio, 'isfuture'):
return asyncio.isfuture(o)
return isinstance(o, asyncio.Future)
def deferred_from_coro(o):
"""Converts a coroutine into a Deferred, or returns the object as is if it isn't a coroutine"""
if isinstance(o, defer.Deferred):
return o
if _isfuture(o) or inspect.isawaitable(o):
if asyncio.isfuture(o) or inspect.isawaitable(o):
if not is_asyncio_reactor_installed():
# wrapping the coroutine directly into a Deferred, this doesn't work correctly with coroutines
# that use asyncio, e.g. "await asyncio.sleep(1)"
@ -167,7 +160,7 @@ def maybeDeferred_coro(f, *args, **kw):
if isinstance(result, defer.Deferred):
return result
elif _isfuture(result) or inspect.isawaitable(result):
elif asyncio.isfuture(result) or inspect.isawaitable(result):
return deferred_from_coro(result)
elif isinstance(result, failure.Failure):
return defer.fail(result)

View File

@ -6,11 +6,10 @@ import struct
from scrapy.utils.decorators import deprecated
# - Python>=3.5 GzipFile's read() has issues returning leftover
# uncompressed data when input is corrupted
# (regression or bug-fix compared to Python 3.4)
# - GzipFile's read() has issues returning leftover uncompressed data when
# input is corrupted
# - read1(), which fetches data before raising EOFError on next call
# works here but is only available from Python>=3.3
# works here
@deprecated('GzipFile.read1')
def read1(gzf, size=-1):
return gzf.read1(size)

View File

@ -82,7 +82,6 @@ setup(
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
@ -92,7 +91,7 @@ setup(
'Topic :: Software Development :: Libraries :: Application Frameworks',
'Topic :: Software Development :: Libraries :: Python Modules',
],
python_requires='>=3.5.2',
python_requires='>=3.6',
install_requires=install_requires,
extras_require=extras_require,
)

View File

@ -2,8 +2,7 @@
attrs
dataclasses; python_version == '3.6'
mitmproxy; python_version >= '3.7'
mitmproxy >= 4, < 5; python_version >= '3.6' and python_version < '3.7'
mitmproxy < 4; python_version < '3.6'
mitmproxy >= 4.0.4, < 5; python_version >= '3.6' and python_version < '3.7'
pyftpdlib
# https://github.com/pytest-dev/pytest-twisted/issues/93
pytest != 5.4, != 5.4.1

View File

@ -1,6 +1,5 @@
import json
import logging
import sys
from ipaddress import IPv4Address
from socket import gethostbyname
from urllib.parse import urlparse
@ -405,7 +404,6 @@ class CrawlSpiderTestCase(TestCase):
self.assertIn("Got response 200", str(log))
self.assertIn({"foo": 42}, items)
@mark.skipif(sys.version_info < (3, 6), reason="Async generators require Python 3.6 or higher")
@mark.only_asyncio()
@defer.inlineCallbacks
def test_async_def_asyncgen_parse(self):
@ -417,7 +415,6 @@ class CrawlSpiderTestCase(TestCase):
itemcount = crawler.stats.get_value('item_scraped_count')
self.assertEqual(itemcount, 1)
@mark.skipif(sys.version_info < (3, 6), reason="Async generators require Python 3.6 or higher")
@mark.only_asyncio()
@defer.inlineCallbacks
def test_async_def_asyncgen_parse_loop(self):
@ -437,7 +434,6 @@ class CrawlSpiderTestCase(TestCase):
for i in range(10):
self.assertIn({'foo': i}, items)
@mark.skipif(sys.version_info < (3, 6), reason="Async generators require Python 3.6 or higher")
@mark.only_asyncio()
@defer.inlineCallbacks
def test_async_def_asyncgen_parse_complex(self):

View File

@ -1,4 +1,3 @@
import sys
import unittest
from unittest import mock
from warnings import catch_warnings
@ -7,9 +6,6 @@ from scrapy.exceptions import ScrapyDeprecationWarning
from scrapy.item import ABCMeta, _BaseItem, BaseItem, DictItem, Field, Item, ItemMeta
PY36_PLUS = (sys.version_info.major >= 3) and (sys.version_info.minor >= 6)
class ItemTest(unittest.TestCase):
def assertSortedEqual(self, first, second, msg=None):
@ -280,14 +276,6 @@ class ItemMetaTest(unittest.TestCase):
with mock.patch.object(base, '__new__', new_mock):
class MyItem(Item):
if not PY36_PLUS:
# This attribute is an internal attribute in Python 3.6+
# and must be propagated properly. See
# https://docs.python.org/3.6/reference/datamodel.html#creating-the-class-object
# In <3.6, we add a dummy attribute just to ensure the
# __new__ method propagates it correctly.
__classcell__ = object()
def f(self):
# For rationale of this see:
# https://github.com/python/cpython/blob/ee1a81b77444c6715cbe610e951c655b6adab88b/Lib/test/test_super.py#L222

View File

@ -7,7 +7,6 @@ from subprocess import Popen, PIPE
from urllib.parse import urlsplit, urlunsplit
from unittest import skipIf
import pytest
from testfixtures import LogCapture
from twisted.internet import defer
from twisted.trial.unittest import TestCase
@ -58,8 +57,6 @@ def _wrong_credentials(proxy_url):
return urlunsplit(bad_auth_proxy)
@skipIf(sys.version_info < (3, 5, 4),
"requires mitmproxy < 3.0.0, which these tests do not support")
@skipIf("pypy" in sys.executable,
"mitmproxy does not support PyPy")
@skipIf(platform.system() == 'Windows' and sys.version_info < (3, 7),
@ -88,14 +85,6 @@ class ProxyConnectTestCase(TestCase):
yield crawler.crawl(self.mockserver.url("/status?n=200", is_secure=True))
self._assert_got_response_code(200, log)
@pytest.mark.xfail(reason='Python 3.6+ fails this earlier', condition=sys.version_info >= (3, 6))
@defer.inlineCallbacks
def test_https_connect_tunnel_error(self):
crawler = get_crawler(SimpleSpider)
with LogCapture() as log:
yield crawler.crawl("https://localhost:99999/status?n=200")
self._assert_got_tunnel_error(log)
@defer.inlineCallbacks
def test_https_tunnel_auth_error(self):
os.environ['https_proxy'] = _wrong_credentials(os.environ['https_proxy'])

View File

@ -89,7 +89,7 @@ deps =
basepython = python3
deps =
{[pinned]deps}
# First lxml version that includes a Windows wheel for Python 3.5, so we do
# First lxml version that includes a Windows wheel for Python 3.6, so we do
# not need to build lxml from sources in a CI Windows job:
lxml==3.8.0