Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop Python 3.7 #7336

Merged
merged 14 commits into from
Jul 3, 2023
4 changes: 1 addition & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,13 @@ jobs:
needs: gen_llhttp
strategy:
matrix:
pyver: [3.7, 3.8, 3.9, '3.10']
pyver: [3.8, 3.9, '3.10']
no-extensions: ['', 'Y']
os: [ubuntu, macos, windows]
experimental: [false]
exclude:
- os: macos
no-extensions: 'Y'
- os: macos
pyver: 3.7
- os: macos
pyver: 3.8
- os: windows
Expand Down
1 change: 1 addition & 0 deletions CHANGES/7336.removal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Dropped support for Python 3.7. -- by :user:`Dreamsorcerer`
6 changes: 1 addition & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,7 @@ define run_tests_in_docker
docker run --rm -ti -v `pwd`:/src -w /src "aiohttp-test-$(1)-$(2)" $(TEST_SPEC)
endef

.PHONY: test-3.7-no-extensions test-3.7 test-3.8-no-extensions test-3.8 test-3.9-no-extensions test-3.9 test-3.10-no-extensions test-3.10
test-3.7-no-extensions:
$(call run_tests_in_docker,3.7,y)
test-3.7:
$(call run_tests_in_docker,3.7,n)
.PHONY: test-3.8-no-extensions test-3.8 test-3.9-no-extensions test
test-3.8-no-extensions:
$(call run_tests_in_docker,3.8,y)
test-3.8:
Expand Down
4 changes: 2 additions & 2 deletions aiohttp/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -993,8 +993,8 @@ def _warn_about_tls_in_tls(
"This support for TLS in TLS is known to be disabled "
"in the stdlib asyncio. This is why you'll probably see "
"an error in the log below.\n\n"
"It is possible to enable it via monkeypatching under "
"Python 3.7 or higher. For more details, see:\n"
"It is possible to enable it via monkeypatching. "
"For more details, see:\n"
"* https://bugs.python.org/issue37179\n"
"* https:/python/cpython/pull/28073\n\n"
"You can temporarily patch this as follows:\n"
Expand Down
17 changes: 1 addition & 16 deletions aiohttp/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
Type,
TypeVar,
Union,
get_args,
overload,
)
from urllib.parse import quote
Expand All @@ -55,14 +56,8 @@
from .log import client_logger
from .typedefs import PathLike # noqa

if sys.version_info >= (3, 8):
from typing import get_args
else:
from typing_extensions import get_args

__all__ = ("BasicAuth", "ChainMapProxy", "ETag")

PY_38 = sys.version_info >= (3, 8)
PY_310 = sys.version_info >= (3, 10)

COOKIE_MAX_LENGTH = 4096
Expand Down Expand Up @@ -113,16 +108,6 @@ def __await__(self) -> Generator[None, None, None]:
yield


if PY_38:
iscoroutinefunction = asyncio.iscoroutinefunction
else:

def iscoroutinefunction(func: Any) -> bool: # type: ignore[misc]
while isinstance(func, functools.partial):
func = func.func
return asyncio.iscoroutinefunction(func)


json_re = re.compile(r"(?:application/|[\w.-]+/[\w.+-]+?\+)json$", re.IGNORECASE)


Expand Down
30 changes: 4 additions & 26 deletions aiohttp/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
Union,
cast,
)
from unittest import mock
from unittest import IsolatedAsyncioTestCase, mock

from aiosignal import Signal
from multidict import CIMultiDict, CIMultiDictProxy
Expand All @@ -34,7 +34,7 @@
from .abc import AbstractCookieJar
from .client_reqrep import ClientResponse
from .client_ws import ClientWebSocketResponse
from .helpers import _SENTINEL, PY_38, sentinel
from .helpers import _SENTINEL, sentinel
from .http import HttpVersion, RawRequestMessage
from .typedefs import StrOrURL
from .web import (
Expand All @@ -54,11 +54,6 @@
else:
SSLContext = None

if PY_38:
from unittest import IsolatedAsyncioTestCase as TestCase
else:
from asynctest import TestCase # type: ignore[no-redef]

REUSE_ADDRESS = os.name == "posix" and sys.platform != "cygwin"


Expand Down Expand Up @@ -405,7 +400,7 @@ async def __aexit__(
await self.close()


class AioHTTPTestCase(TestCase, ABC):
class AioHTTPTestCase(IsolatedAsyncioTestCase, ABC):
"""A base class to allow for unittest web applications using aiohttp.

Provides the following:
Expand All @@ -426,21 +421,13 @@ async def get_application(self) -> Application:
object to test.
"""

def setUp(self) -> None:
if not PY_38:
asyncio.get_event_loop().run_until_complete(self.asyncSetUp())

async def asyncSetUp(self) -> None:
self.app = await self.get_application()
self.server = await self.get_server(self.app)
self.client = await self.get_client(self.server)

await self.client.start_server()

def tearDown(self) -> None:
if not PY_38:
asyncio.get_event_loop().run_until_complete(self.asyncTearDown())

async def asyncTearDown(self) -> None:
await self.client.close()

Expand Down Expand Up @@ -487,16 +474,7 @@ def setup_test_loop(
asyncio.set_event_loop(loop)
if sys.platform != "win32" and not skip_watcher:
policy = asyncio.get_event_loop_policy()
watcher: asyncio.AbstractChildWatcher
try: # Python >= 3.8
# Refs:
# * https:/pytest-dev/pytest-xdist/issues/620
# * https://stackoverflow.com/a/58614689/595220
# * https://bugs.python.org/issue35621
# * https:/python/cpython/pull/14344
watcher = asyncio.ThreadedChildWatcher()
except AttributeError: # Python < 3.8
watcher = asyncio.SafeChildWatcher()
watcher = asyncio.ThreadedChildWatcher()
watcher.attach_loop(loop)
with contextlib.suppress(NotImplementedError):
policy.set_child_watcher(watcher)
Expand Down
9 changes: 1 addition & 8 deletions aiohttp/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,15 +409,8 @@ async def _run_app(
)

# sleep forever by 1 hour intervals,
# on Windows before Python 3.8 wake up every 1 second to handle
# Ctrl+C smoothly
if sys.platform == "win32" and sys.version_info < (3, 8):
delay = 1
else:
delay = 3600

while True:
await asyncio.sleep(delay)
await asyncio.sleep(3600)
finally:
await runner.cleanup()

Expand Down
8 changes: 0 additions & 8 deletions aiohttp/web_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import warnings
from concurrent.futures import Executor
from http import HTTPStatus
from http.cookies import Morsel
from typing import (
TYPE_CHECKING,
Any,
Expand All @@ -27,7 +26,6 @@
from .compression_utils import ZLibCompressor
from .helpers import (
ETAG_ANY,
PY_38,
QUOTED_ETAG_RE,
CookieMixin,
ETag,
Expand All @@ -53,12 +51,6 @@
BaseClass = collections.abc.MutableMapping


if not PY_38:
# allow samesite to be used in python < 3.8
# already permitted in python 3.8, see https://bugs.python.org/issue29613
Morsel._reserved["samesite"] = "SameSite" # type: ignore[attr-defined]


class ContentCoding(enum.Enum):
# The content codings that we have support for.
#
Expand Down
5 changes: 1 addition & 4 deletions aiohttp/web_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,8 @@ async def stop(self) -> None:

async def _wait(self, parent_task: Optional["asyncio.Task[object]"]) -> None:
exclude = self._runner.starting_tasks | {asyncio.current_task(), parent_task}
# TODO(PY38): while tasks := asyncio.all_tasks() - exclude:
tasks = asyncio.all_tasks() - exclude
while tasks:
while tasks := asyncio.all_tasks() - exclude:
await asyncio.wait(tasks)
tasks = asyncio.all_tasks() - exclude


class TCPSite(BaseSite):
Expand Down
7 changes: 4 additions & 3 deletions aiohttp/web_urldispatcher.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import abc
import asyncio
import base64
import hashlib
import keyword
Expand Down Expand Up @@ -35,7 +36,7 @@

from . import hdrs
from .abc import AbstractMatchInfo, AbstractRouter, AbstractView
from .helpers import DEBUG, iscoroutinefunction
from .helpers import DEBUG
from .http import HttpVersion11
from .typedefs import Handler, PathLike
from .web_exceptions import (
Expand Down Expand Up @@ -164,15 +165,15 @@ def __init__(
if expect_handler is None:
expect_handler = _default_expect_handler

assert iscoroutinefunction(
assert asyncio.iscoroutinefunction(
expect_handler
), f"Coroutine is expected, got {expect_handler!r}"

method = method.upper()
if not HTTP_METHOD_RE.match(method):
raise ValueError(f"{method} is not allowed HTTP method")

if iscoroutinefunction(handler):
if asyncio.iscoroutinefunction(handler):
pass
elif isinstance(handler, type) and issubclass(handler, AbstractView):
pass
Expand Down
6 changes: 0 additions & 6 deletions aiohttp/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,12 +178,6 @@ def init_signals(self) -> None:
signal.siginterrupt(signal.SIGUSR1, False)
# Reset signals so Gunicorn doesn't swallow subprocess return codes
# See: https:/aio-libs/aiohttp/issues/6130
if sys.version_info < (3, 8):
# Starting from Python 3.8,
# the default child watcher is ThreadedChildWatcher.
# The watcher doesn't depend on SIGCHLD signal,
# there is no need to reset it.
signal.signal(signal.SIGCHLD, signal.SIG_DFL)

def handle_quit(self, sig: int, frame: Optional[FrameType]) -> None:
self.alive = False
Expand Down
3 changes: 1 addition & 2 deletions docs/client_advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -611,8 +611,7 @@ Proxy credentials are given from ``~/.netrc`` file if present (see

.. attention::

CPython has introduced the support for TLS in TLS around Python 3.7.
But, as of now (Python 3.10), it's disabled for the transports that
As of now (Python 3.10), support for TLS in TLS is disabled for the transports that
:py:mod:`asyncio` uses. If the further release of Python (say v3.11)
toggles one attribute, it'll *just work™*.

Expand Down
69 changes: 2 additions & 67 deletions docs/testing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -396,73 +396,8 @@ functionality, the AioHTTPTestCase is provided::

``await super().asyncTearDown()`` call is required.

Patching unittest test cases
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Patching test cases is tricky, when using python older than 3.8 :py:func:`~unittest.mock.patch` does not behave as it has to.
We recommend using :py:mod:`asynctest` that provides :py:func:`~asynctest.patch` that is capable of creating
a magic mock that supports async. It can be used with a decorator as well as with a context manager:

.. code-block:: python
:emphasize-lines: 1,37,46

from asynctest.mock import patch as async_patch

from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
from aiohttp.web_app import Application
from aiohttp.web_request import Request
from aiohttp.web_response import Response
from aiohttp.web_routedef import get


async def do_something():
print('something')


async def ping(request: Request) -> Response:
await do_something()
return Response(text='pong')


class TestApplication(AioHTTPTestCase):
def get_app(self) -> Application:
app = Application()
app.router.add_routes([
get('/ping/', ping)
])

return app

@unittest_run_loop
async def test_ping(self):
resp = await self.client.get('/ping/')

self.assertEqual(resp.status, 200)
self.assertEqual(await resp.text(), 'pong')

@unittest_run_loop
async def test_ping_mocked_do_something(self):
with async_patch('tests.do_something') as do_something_patch:
resp = await self.client.get('/ping/')

self.assertEqual(resp.status, 200)
self.assertEqual(await resp.text(), 'pong')

self.assertTrue(do_something_patch.called)

@unittest_run_loop
@async_patch('tests.do_something')
async def test_ping_mocked_do_something_decorated(self, do_something_patch):
resp = await self.client.get('/ping/')

self.assertEqual(resp.status, 200)
self.assertEqual(await resp.text(), 'pong')

self.assertTrue(do_something_patch.called)


Faking request object
---------------------
^^^^^^^^^^^^^^^^^^^^^

aiohttp provides test utility for creating fake
:class:`aiohttp.web.Request` objects:
Expand Down Expand Up @@ -561,7 +496,7 @@ conditions that hard to reproduce on real server::


Framework Agnostic Utilities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
----------------------------

High level test creation::

Expand Down
3 changes: 1 addition & 2 deletions docs/third_party.rst
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ support to aiohttp web servers.

- `aiohttp-pydantic <https:/Maillol/aiohttp-pydantic>`_
An ``aiohttp.View`` to validate the HTTP request's body, query-string, and
headers regarding function annotations and generate OpenAPI doc. Python 3.8+
required.
headers regarding function annotations and generate OpenAPI doc.

- `aiohttp-swagger <https:/cr0hn/aiohttp-swagger>`_
Swagger API Documentation builder for aiohttp server.
Expand Down
1 change: 0 additions & 1 deletion requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
aiodns==3.0.0; sys_platform=="linux" or sys_platform=="darwin"
aiosignal==1.2.0
async-timeout==4.0.2
asynctest==0.13.0; python_version<"3.8"
Brotli==1.0.9
cchardet==2.1.7; python_version < "3.10" # Unmaintained: aio-libs/aiohttp#6819
charset-normalizer==2.0.12
Expand Down
Loading