Skip to content

Commit f2556ea

Browse files
authored
Merge pull request #225 from scrapinghub/scrapy-2.13
Adapt tests to Scrapy 2.13, improve coverage reporting
2 parents b68c90e + 0b1fd5a commit f2556ea

File tree

8 files changed

+80
-31
lines changed

8 files changed

+80
-31
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
- python-version: "3.12"
3131
- python-version: "3.13"
3232
- python-version: "3.13"
33-
toxenv: "asyncio"
33+
toxenv: "default-reactor"
3434

3535
steps:
3636
- uses: actions/checkout@v4

pyproject.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,21 @@ regex = true
1616
[[tool.bumpversion.files]]
1717
filename = "scrapy_poet/VERSION"
1818

19+
[tool.coverage.run]
20+
branch = true
21+
22+
[tool.coverage.paths]
23+
source = [
24+
"scrapy_poet",
25+
".tox/**/site-packages/scrapy_poet"
26+
]
27+
28+
[tool.coverage.report]
29+
exclude_also = [
30+
"if TYPE_CHECKING:",
31+
"@(abc\\.)?abstractmethod",
32+
]
33+
1934
[tool.isort]
2035
profile = "black"
2136
multi_line_output = 3

scrapy_poet/utils/mockserver.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from importlib import import_module
66
from subprocess import PIPE, Popen
77

8-
from twisted.internet import reactor
98
from twisted.web.server import Site
109

1110

@@ -48,6 +47,8 @@ def __exit__(self, exc_type, exc_value, traceback):
4847

4948

5049
def main():
50+
from twisted.internet import reactor
51+
5152
parser = argparse.ArgumentParser()
5253
parser.add_argument("resource")
5354
parser.add_argument("--port", type=int)

scrapy_poet/utils/testing.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from scrapy.settings import Settings
1010
from scrapy.utils.python import to_bytes
1111
from scrapy.utils.test import get_crawler as _get_crawler
12-
from twisted.internet import reactor
1312
from twisted.internet.defer import inlineCallbacks
1413
from twisted.internet.task import deferLater
1514
from twisted.web.resource import Resource
@@ -38,6 +37,8 @@ class LeafResource(Resource):
3837
isLeaf = True
3938

4039
def deferRequest(self, request, delay, f, *a, **kw):
40+
from twisted.internet import reactor
41+
4142
def _cancelrequest(_):
4243
# silence CancelledError
4344
d.addErrback(lambda _: None)
@@ -264,6 +265,13 @@ def _get_test_settings():
264265
settings["ADDONS"] = {
265266
"scrapy_poet.Addon": 300,
266267
}
268+
try:
269+
from scrapy.utils.test import get_reactor_settings
270+
271+
settings.update(get_reactor_settings())
272+
except ImportError:
273+
# Scrapy < 2.13.0, no need to change the reactor settings
274+
pass
267275
return settings
268276

269277

tests/__init__.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +0,0 @@
1-
import os
2-
3-
# Note that tox.ini should only set the REACTOR env variable when running
4-
# pytest with "--reactor=asyncio".
5-
if os.environ.get("REACTOR", "") == "asyncio":
6-
from scrapy.utils.reactor import install_reactor
7-
8-
install_reactor("twisted.internet.asyncioreactor.AsyncioSelectorReactor")

tests/test_middleware.py

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import os
12
import socket
2-
from pathlib import Path
3+
import subprocess
4+
import sys
35
from textwrap import dedent
46
from typing import Optional, Type, Union
57

@@ -11,7 +13,6 @@
1113
from scrapy import Request
1214
from scrapy.http import Response
1315
from scrapy.utils.log import configure_logging
14-
from scrapy.utils.testproc import ProcessTest
1516
from twisted.internet.threads import deferToThread
1617
from url_matcher.util import get_domain
1718
from web_poet import ApplyRule, HttpResponse, ItemPage, RequestUrl, ResponseUrl, WebPage
@@ -541,7 +542,6 @@ def test_skip_download_request_url_page(settings):
541542
assert crawler.stats.get_stats().get("downloader/response_count", 0) == 0
542543

543544

544-
@inlineCallbacks
545545
def test_scrapy_shell(tmp_path):
546546
try:
547547
import scrapy.addons # noqa: F401
@@ -564,13 +564,33 @@ def test_scrapy_shell(tmp_path):
564564
}
565565
"""
566566
settings = dedent(settings)
567-
Path(tmp_path, "settings.py").write_text(settings)
568-
pt = ProcessTest()
569-
pt.command = "shell"
570-
pt.cwd = tmp_path
567+
(tmp_path / "settings.py").write_text(settings)
568+
569+
env = os.environ.copy()
570+
env["SCRAPY_SETTINGS_MODULE"] = "settings"
571571
with MockServer(EchoResource) as server:
572-
_, out, err = yield pt.execute(
573-
[server.root_url, "-c", "item"], settings="settings"
572+
args = (
573+
sys.executable,
574+
"-m",
575+
"scrapy.cmdline",
576+
"shell",
577+
server.root_url,
578+
"-c",
579+
"item",
574580
)
581+
p = subprocess.Popen(
582+
args,
583+
cwd=tmp_path,
584+
env=env,
585+
stdout=subprocess.PIPE,
586+
stderr=subprocess.PIPE,
587+
)
588+
try:
589+
out, err = p.communicate(timeout=15)
590+
except subprocess.TimeoutExpired:
591+
p.kill()
592+
p.communicate()
593+
pytest.fail("Command took too much time to complete")
594+
575595
assert b"Using DummyResponse instead of downloading" not in err
576596
assert b"{}" in out # noqa: P103

tests/test_web_poet_rules.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
import attrs
1616
import pytest
1717
import scrapy
18+
from packaging.version import Version
1819
from pytest_twisted import inlineCallbacks
20+
from scrapy import __version__ as SCRAPY_VERSION
1921
from url_matcher import Patterns
2022
from url_matcher.util import get_domain
2123
from web_poet import (
@@ -916,10 +918,15 @@ def test_item_return_from_injectable() -> None:
916918
assert item == ProductFromInjectable(name="product from injectable")
917919
assert_deps(deps, {"item": ProductFromInjectable})
918920

919-
# calling the actual page object should not work since it's not inheriting
920-
# from ``web_poet.ItemPage``.
921+
# calling the actual page object should not work in the same way since it's
922+
# not inheriting from ``web_poet.ItemPage``.
921923
item, deps = yield crawl_item_and_deps(ProductFromInjectablePage)
922-
assert item is None
924+
if Version(SCRAPY_VERSION) < Version("2.13"):
925+
# older Scrapy refuses to return a ProductFromInjectablePage instance
926+
assert item is None
927+
else:
928+
# newer Scrapy returns it
929+
assert isinstance(item, ProductFromInjectablePage)
923930

924931
# However, the page object should still be injected into the callback method.
925932
assert_deps(deps, {"item": ProductFromInjectablePage})

tox.ini

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ deps =
77
pytest-cov
88
pytest-twisted
99
Twisted
10-
Scrapy < 2.13.0
10+
setenv =
11+
REACTOR=asyncio
1112
commands =
1213
py.test \
13-
--cov-report=term --cov-report=html --cov-report= --cov-report=xml --cov=scrapy_poet \
14+
--cov-config=pyproject.toml --cov-report=term --cov-report=html --cov-report= --cov-report=xml --cov=scrapy_poet \
1415
--doctest-modules \
16+
--reactor=asyncio \
1517
{posargs:scrapy_poet tests}
1618

1719
[pinned]
@@ -32,6 +34,8 @@ deps =
3234

3335
[testenv:min]
3436
basepython = python3.9
37+
setenv =
38+
REACTOR=
3539
deps =
3640
{[pinned]deps}
3741
scrapy==2.6.0
@@ -46,6 +50,8 @@ deps =
4650
# See: https://github.com/scrapinghub/scrapy-poet/issues/48
4751
[testenv:pinned-scrapy-2x7]
4852
basepython=python3.9
53+
setenv =
54+
REACTOR=
4955
deps =
5056
{[pinned]deps}
5157
scrapy==2.7.0
@@ -55,25 +61,25 @@ deps =
5561
# See: https://github.com/scrapinghub/scrapy-poet/issues/118
5662
[testenv:pinned-scrapy-2x8]
5763
basepython=python3.9
64+
setenv =
65+
REACTOR=
5866
deps =
5967
{[pinned]deps}
6068
scrapy==2.8.0
6169
Twisted<23.8.0
6270

63-
[testenv:asyncio]
71+
[testenv:default-reactor]
6472
setenv =
65-
REACTOR=asyncio
73+
REACTOR=
6674
commands =
67-
{[testenv]commands} --reactor=asyncio
75+
{[testenv]commands} --reactor=default
6876
deps =
6977
{[testenv]deps}
7078

7179
[testenv:asyncio-min]
7280
basepython = python3.9
73-
setenv =
74-
{[testenv:asyncio]setenv}
7581
commands =
76-
{[testenv:asyncio]commands}
82+
{[testenv]commands}
7783
deps =
7884
{[testenv:min]deps}
7985

0 commit comments

Comments
 (0)