From 7cec83658fb6d0d3c40b8e84ef6a8d4b6559a3f7 Mon Sep 17 00:00:00 2001
From: Sam Bull <git@sambull.org>
Date: Tue, 20 Aug 2024 21:40:02 +0100
Subject: [PATCH] Drop Python 3.8 (#8797)

(cherry picked from commit 5be5af3c900ef9ead3387a1193fc4ff4ad1e5594)
---
 .codecov.yml                | 2 +-
 .github/workflows/ci-cd.yml | 4 +---
 CHANGES/8797.breaking.rst   | 1 +
 Makefile                    | 6 +-----
 aiohttp/cookiejar.py        | 2 +-
 aiohttp/resolver.py         | 6 ++----
 aiohttp/web_fileresponse.py | 4 ----
 setup.cfg                   | 3 +--
 setup.py                    | 4 ++--
 tests/test_resolver.py      | 7 -------
 10 files changed, 10 insertions(+), 29 deletions(-)
 create mode 100644 CHANGES/8797.breaking.rst

diff --git a/.codecov.yml b/.codecov.yml
index 30809053e16..e21d45ac7b2 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -1,5 +1,5 @@
 codecov:
-  branch: 3.9
+  branch: master
   notify:
     after_n_builds: 13
 
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index a6a58cef9c2..d422a269f02 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -125,15 +125,13 @@ jobs:
     needs: gen_llhttp
     strategy:
       matrix:
-        pyver: [3.8, 3.9, '3.10', '3.11', '3.12']
+        pyver: [3.9, '3.10', '3.11', '3.12']
         no-extensions: ['', 'Y']
         os: [ubuntu, macos, windows]
         experimental: [false]
         exclude:
           - os: macos
             no-extensions: 'Y'
-          - os: macos
-            pyver: 3.8
           - os: windows
             no-extensions: 'Y'
         include:
diff --git a/CHANGES/8797.breaking.rst b/CHANGES/8797.breaking.rst
new file mode 100644
index 00000000000..c219ea3d264
--- /dev/null
+++ b/CHANGES/8797.breaking.rst
@@ -0,0 +1 @@
+Dropped support for Python 3.8 -- by :user:`Dreamsorcerer`.
diff --git a/Makefile b/Makefile
index bb2d437a134..2a40be049ee 100644
--- a/Makefile
+++ b/Makefile
@@ -112,11 +112,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.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:
-	$(call run_tests_in_docker,3.8,n)
+.PHONY: test-3.9-no-extensions test
 test-3.9-no-extensions:
 	$(call run_tests_in_docker,3.9,y)
 test-3.9:
diff --git a/aiohttp/cookiejar.py b/aiohttp/cookiejar.py
index e9997ce2935..e3eefc9c656 100644
--- a/aiohttp/cookiejar.py
+++ b/aiohttp/cookiejar.py
@@ -70,7 +70,7 @@ class CookieJar(AbstractCookieJar):
     except (OSError, ValueError):
         # Hit the maximum representable time on Windows
         # https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/localtime-localtime32-localtime64
-        # Throws ValueError on PyPy 3.8 and 3.9, OSError elsewhere
+        # Throws ValueError on PyPy 3.9, OSError elsewhere
         MAX_TIME = calendar.timegm((3000, 12, 31, 23, 59, 59, -1, -1, -1))
     except OverflowError:
         # #4515: datetime.max may not be representable on 32-bit platforms
diff --git a/aiohttp/resolver.py b/aiohttp/resolver.py
index 10e36266abe..c8fce5b5706 100644
--- a/aiohttp/resolver.py
+++ b/aiohttp/resolver.py
@@ -1,6 +1,5 @@
 import asyncio
 import socket
-import sys
 from typing import Any, Dict, List, Optional, Tuple, Type, Union
 
 from .abc import AbstractResolver, ResolveResult
@@ -18,7 +17,6 @@
 
 
 _NUMERIC_SOCKET_FLAGS = socket.AI_NUMERICHOST | socket.AI_NUMERICSERV
-_SUPPORTS_SCOPE_ID = sys.version_info >= (3, 9, 0)
 
 
 class ThreadedResolver(AbstractResolver):
@@ -49,7 +47,7 @@ async def resolve(
                     # IPv6 is not supported by Python build,
                     # or IPv6 is not enabled in the host
                     continue
-                if address[3] and _SUPPORTS_SCOPE_ID:
+                if address[3]:
                     # This is essential for link-local IPv6 addresses.
                     # LL IPv6 is a VERY rare case. Strictly speaking, we should use
                     # getnameinfo() unconditionally, but performance makes sense.
@@ -116,7 +114,7 @@ async def resolve(
             address: Union[Tuple[bytes, int], Tuple[bytes, int, int, int]] = node.addr
             family = node.family
             if family == socket.AF_INET6:
-                if len(address) > 3 and address[3] and _SUPPORTS_SCOPE_ID:
+                if len(address) > 3 and address[3]:
                     # This is essential for link-local IPv6 addresses.
                     # LL IPv6 is a VERY rare case. Strictly speaking, we should use
                     # getnameinfo() unconditionally, but performance makes sense.
diff --git a/aiohttp/web_fileresponse.py b/aiohttp/web_fileresponse.py
index 0c23e375d25..2c253e03b0a 100644
--- a/aiohttp/web_fileresponse.py
+++ b/aiohttp/web_fileresponse.py
@@ -1,7 +1,6 @@
 import asyncio
 import os
 import pathlib
-import sys
 from contextlib import suppress
 from mimetypes import MimeTypes
 from stat import S_ISREG
@@ -48,9 +47,6 @@
 
 CONTENT_TYPES: Final[MimeTypes] = MimeTypes()
 
-if sys.version_info < (3, 9):
-    CONTENT_TYPES.encodings_map[".br"] = "br"
-
 # File extension to IANA encodings map that will be checked in the order defined.
 ENCODING_EXTENSIONS = MappingProxyType(
     {ext: CONTENT_TYPES.encodings_map[ext] for ext in (".br", ".gz")}
diff --git a/setup.cfg b/setup.cfg
index cfd1be5610f..03fb594ecbe 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -33,7 +33,6 @@ classifiers =
 
   Programming Language :: Python
   Programming Language :: Python :: 3
-  Programming Language :: Python :: 3.8
   Programming Language :: Python :: 3.9
   Programming Language :: Python :: 3.10
   Programming Language :: Python :: 3.11
@@ -42,7 +41,7 @@ classifiers =
   Topic :: Internet :: WWW/HTTP
 
 [options]
-python_requires = >=3.8
+python_requires = >=3.9
 packages = aiohttp
 # https://setuptools.readthedocs.io/en/latest/setuptools.html#setting-the-zip-safe-flag
 zip_safe = False
diff --git a/setup.py b/setup.py
index 23ac86f9b4a..808f539d259 100644
--- a/setup.py
+++ b/setup.py
@@ -4,8 +4,8 @@
 
 from setuptools import Extension, setup
 
-if sys.version_info < (3, 8):
-    raise RuntimeError("aiohttp 3.x requires Python 3.8+")
+if sys.version_info < (3, 9):
+    raise RuntimeError("aiohttp 3.x requires Python 3.9+")
 
 
 NO_EXTENSIONS: bool = bool(os.environ.get("AIOHTTP_NO_EXTENSIONS"))
diff --git a/tests/test_resolver.py b/tests/test_resolver.py
index f51506a6999..825db81e41b 100644
--- a/tests/test_resolver.py
+++ b/tests/test_resolver.py
@@ -9,7 +9,6 @@
 
 from aiohttp.resolver import (
     _NUMERIC_SOCKET_FLAGS,
-    _SUPPORTS_SCOPE_ID,
     AsyncResolver,
     DefaultResolver,
     ThreadedResolver,
@@ -136,9 +135,6 @@ async def test_async_resolver_positive_ipv4_lookup(loop: Any) -> None:
 
 
 @pytest.mark.skipif(not getaddrinfo, reason="aiodns >=3.2.0 required")
-@pytest.mark.skipif(
-    not _SUPPORTS_SCOPE_ID, reason="python version does not support scope id"
-)
 async def test_async_resolver_positive_link_local_ipv6_lookup(loop: Any) -> None:
     with patch("aiodns.DNSResolver") as mock:
         mock().getaddrinfo.return_value = fake_aiodns_getaddrinfo_ipv6_result(
@@ -211,9 +207,6 @@ async def test_threaded_resolver_positive_lookup() -> None:
     ipaddress.ip_address(real[0]["host"])
 
 
-@pytest.mark.skipif(
-    not _SUPPORTS_SCOPE_ID, reason="python version does not support scope id"
-)
 async def test_threaded_resolver_positive_ipv6_link_local_lookup() -> None:
     loop = Mock()
     loop.getaddrinfo = fake_ipv6_addrinfo(["fe80::1"])