Fixes for Python 3.14. (#7460)

This commit is contained in:
Andrey Rakhmatullin 2026-04-27 18:47:52 +05:00 committed by GitHub
parent fc4c57e795
commit 068aa69b35
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 15 additions and 27 deletions

View File

@ -167,7 +167,7 @@ class StdoutFeedStorage:
@implementer(IFeedStorage) @implementer(IFeedStorage)
class FileFeedStorage: class FileFeedStorage:
def __init__(self, uri: str, *, feed_options: dict[str, Any] | None = None): def __init__(self, uri: str, *, feed_options: dict[str, Any] | None = None):
self.path: str = file_uri_to_path(uri) if uri.startswith("file://") else uri self.path: str = file_uri_to_path(uri) if uri.startswith("file:") else uri
feed_options = feed_options or {} feed_options = feed_options or {}
self.write_mode: OpenBinaryMode = ( self.write_mode: OpenBinaryMode = (
"wb" if feed_options.get("overwrite", False) else "ab" "wb" if feed_options.get("overwrite", False) else "ab"

View File

@ -4,7 +4,7 @@ import sys
from io import StringIO from io import StringIO
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from unittest import TestCase from unittest import TestCase
from unittest.mock import Mock, PropertyMock, call, patch from unittest.mock import MagicMock, Mock, PropertyMock, call, patch
from scrapy.commands.check import Command, TextTestResult from scrapy.commands.check import Command, TextTestResult
from tests.test_commands import TestProjectBase from tests.test_commands import TestProjectBase
@ -133,7 +133,7 @@ class CheckSpider(scrapy.Spider):
def test_printSummary_with_unsuccessful_test_result_without_errors_and_without_failures( def test_printSummary_with_unsuccessful_test_result_without_errors_and_without_failures(
self, self,
) -> None: ) -> None:
result = TextTestResult(Mock(), descriptions=False, verbosity=1) result = TextTestResult(MagicMock(), descriptions=False, verbosity=1)
start_time = 1.0 start_time = 1.0
stop_time = 2.0 stop_time = 2.0
result.testsRun = 5 result.testsRun = 5
@ -147,7 +147,7 @@ class CheckSpider(scrapy.Spider):
def test_printSummary_with_unsuccessful_test_result_with_only_failures( def test_printSummary_with_unsuccessful_test_result_with_only_failures(
self, self,
) -> None: ) -> None:
result = TextTestResult(Mock(), descriptions=False, verbosity=1) result = TextTestResult(MagicMock(), descriptions=False, verbosity=1)
start_time = 1.0 start_time = 1.0
stop_time = 2.0 stop_time = 2.0
result.testsRun = 5 result.testsRun = 5
@ -158,7 +158,7 @@ class CheckSpider(scrapy.Spider):
mock_write.assert_called_with(" (failures=1)") mock_write.assert_called_with(" (failures=1)")
def test_printSummary_with_unsuccessful_test_result_with_only_errors(self) -> None: def test_printSummary_with_unsuccessful_test_result_with_only_errors(self) -> None:
result = TextTestResult(Mock(), descriptions=False, verbosity=1) result = TextTestResult(MagicMock(), descriptions=False, verbosity=1)
start_time = 1.0 start_time = 1.0
stop_time = 2.0 stop_time = 2.0
result.testsRun = 5 result.testsRun = 5
@ -171,7 +171,7 @@ class CheckSpider(scrapy.Spider):
def test_printSummary_with_unsuccessful_test_result_with_both_failures_and_errors( def test_printSummary_with_unsuccessful_test_result_with_both_failures_and_errors(
self, self,
) -> None: ) -> None:
result = TextTestResult(Mock(), descriptions=False, verbosity=1) result = TextTestResult(MagicMock(), descriptions=False, verbosity=1)
start_time = 1.0 start_time = 1.0
stop_time = 2.0 stop_time = 2.0
result.testsRun = 5 result.testsRun = 5

View File

@ -44,7 +44,7 @@ if TYPE_CHECKING:
from collections.abc import Callable, Iterable from collections.abc import Callable, Iterable
def path_to_url(path: Path) -> str: def path_to_url(path: str | Path) -> str:
return urljoin("file:", pathname2url(str(path))) return urljoin("file:", pathname2url(str(path)))
@ -1293,7 +1293,7 @@ class TestFeedExporterSignals:
with tempfile.NamedTemporaryFile(suffix="json") as tmp: with tempfile.NamedTemporaryFile(suffix="json") as tmp:
settings = { settings = {
"FEEDS": { "FEEDS": {
f"file:///{tmp.name}": { printf_escape(path_to_url(tmp.name)): {
"format": "json", "format": "json",
}, },
}, },

View File

@ -115,8 +115,8 @@ class TestFilesPipeline:
req1 = Request("http://foo.bar/baz.txt?fizz") req1 = Request("http://foo.bar/baz.txt?fizz")
assert file_path(req1) == "full/a2b4913a62f65445aeae2bac08cd8c3b41d7195e.txt" assert file_path(req1) == "full/a2b4913a62f65445aeae2bac08cd8c3b41d7195e.txt"
req2 = Request("http://foo.bar/get_img.php?file=photo.jpg") req2 = Request("http://foo.bar/get_img.foo?file=photo.jpg")
assert file_path(req2) == "full/118230fd648f1080c81c234d5e2463ea496f8c05.jpg" assert file_path(req2) == "full/7fc9461c9fd836515bea6983373097203a7d748e.jpg"
def test_file_path(self): def test_file_path(self):
file_path = self.pipeline.file_path file_path = self.pipeline.file_path
@ -141,10 +141,10 @@ class TestFilesPipeline:
assert ( assert (
file_path( file_path(
Request( Request(
"http://www.dfsonline.co.uk/get_prod_image.php?img=status_0907_mdm.jpg" "http://www.dfsonline.co.uk/get_prod_image?img=status_0907_mdm.jpg"
) )
) )
== "full/4507be485f38b0da8a0be9eb2e1dfab8a19223f2.jpg" == "full/c67f916ff9d542e822dedf38f9fcb146d1faba78.jpg"
) )
assert ( assert (
file_path(Request("http://www.dorma.co.uk/images/product_details/2532/")) file_path(Request("http://www.dorma.co.uk/images/product_details/2532/"))
@ -165,10 +165,10 @@ class TestFilesPipeline:
assert ( assert (
file_path( file_path(
Request( Request(
"http://www.dfsonline.co.uk/get_prod_image.php?img=status_0907_mdm.jpg.bohaha" "http://www.dfsonline.co.uk/get_prod_image?img=status_0907_mdm.jpg.bohaha"
) )
) )
== "full/76c00cef2ef669ae65052661f68d451162829507" == "full/e75f2fa260521b56f6b6a867447b8002d00b5841"
) )
assert ( assert (
file_path( file_path(

View File

@ -120,7 +120,7 @@ class PickleFifoDiskQueueTest(t.FifoDiskQueueTest, FifoDiskQueueTestMixin):
match=r"Can't (get|pickle) local object|Can't pickle .*: it's not found as", match=r"Can't (get|pickle) local object|Can't pickle .*: it's not found as",
) as exc_info: ) as exc_info:
q.push(lambda x: x) q.push(lambda x: x)
if hasattr(sys, "pypy_version_info"): if sys.version_info >= (3, 14) or hasattr(sys, "pypy_version_info"):
assert isinstance(exc_info.value.__context__, pickle.PicklingError) assert isinstance(exc_info.value.__context__, pickle.PicklingError)
else: else:
assert isinstance(exc_info.value.__context__, AttributeError) assert isinstance(exc_info.value.__context__, AttributeError)

View File

@ -1,5 +1,4 @@
import asyncio import asyncio
import warnings
import pytest import pytest
@ -18,17 +17,6 @@ class TestAsyncio:
# the result should depend only on the pytest --reactor argument # the result should depend only on the pytest --reactor argument
assert is_asyncio_reactor_installed() == (reactor_pytest == "asyncio") assert is_asyncio_reactor_installed() == (reactor_pytest == "asyncio")
@pytest.mark.requires_reactor # installs a reactor
def test_install_asyncio_reactor(self):
from twisted.internet import reactor as original_reactor
with warnings.catch_warnings(record=True) as w:
install_reactor(_asyncio_reactor_path)
assert len(w) == 0, [str(warning) for warning in w]
from twisted.internet import reactor # pylint: disable=reimported
assert original_reactor == reactor
@pytest.mark.requires_reactor # installs a reactor @pytest.mark.requires_reactor # installs a reactor
@pytest.mark.only_asyncio @pytest.mark.only_asyncio
@coroutine_test @coroutine_test