Skip to content

Commit dfa2697

Browse files
committed
Added test for limit_twisted_callback_hell
1 parent ae6e28c commit dfa2697

6 files changed

+106
-22
lines changed

patterns/inequal_producer_consumer.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ async def consumer(
4646
sys.exit(0)
4747

4848

49-
async def main() -> None:
49+
async def orchestrator() -> None:
5050
result_queue = asyncio.Queue() # type: asyncio.Queue[int]
5151
event = asyncio.Event() # type: asyncio.Event
5252
limit = asyncio.Semaphore(5) # type: asyncio.Semaphore
@@ -63,4 +63,4 @@ async def main() -> None:
6363

6464

6565
if __name__ == "__main__":
66-
asyncio.run(main())
66+
asyncio.run(orchestrator())

patterns/twisted_callback_hell.py

+25-16
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,43 @@
11
from twisted.internet import defer, reactor
22

33

4-
def get_dummy_data(input_data):
4+
def get_dummy_number(
5+
input_number: int, defer_duration: float = 2, multiplier: float = 3
6+
) -> defer.Deferred:
57
"""
68
This function is a dummy which simulates a delayed result and
7-
returns a Deferred which will fire with that result. Don't try too
8-
hard to understand this.
9+
returns a Deferred which will fire with that result.
910
"""
10-
print("get_dummy_data called")
11+
12+
print("get_dummy_number called")
13+
1114
deferred = defer.Deferred()
12-
# simulate a delayed result by asking the reactor to fire the
13-
# Deferred in 2 seconds time with the result inputData * 3
14-
reactor.callLater(2, deferred.callback, input_data * 3)
15+
# Simulate a delayed result by asking the reactor to fire the
16+
# Deferred in '2 seconds' time with the result 'input_number * 3'.
17+
reactor.callLater(defer_duration, deferred.callback, input_number * multiplier)
1518
return deferred
1619

1720

18-
def cb_print_data(result):
21+
def cb_print_number(result: int) -> None:
1922
"""
2023
Data handling function to be added as a callback: handles the
2124
data by printing the result
2225
"""
2326
print(f"Result received: {result}")
2427

2528

26-
deferred = get_dummy_data(3)
27-
deferred.addCallback(cb_print_data)
29+
def orchestrator(stop_after: float = 4) -> None:
30+
deferred = get_dummy_number(3)
31+
deferred.addCallback(cb_print_number)
32+
33+
# Manually set up the end of the process by asking the reactor to
34+
# stop itself in 4 seconds time.
35+
reactor.callLater(stop_after, reactor.stop)
36+
37+
# Start up the Twisted reactor (event loop handler) manually.
38+
print("Starting the reactor")
39+
reactor.run()
40+
2841

29-
# manually set up the end of the process by asking the reactor to
30-
# stop itself in 4 seconds time
31-
reactor.callLater(4, reactor.stop)
32-
# start up the Twisted reactor (event loop handler) manually
33-
print("Starting the reactor")
34-
reactor.run()
42+
if __name__ == "__main__":
43+
orchestrator()

requirements.in

+1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ aiobotocore
22
aioredis
33
httpx
44
rich
5+
twisted
56
uvloop

requirements.txt

+24-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@ async-timeout==3.0.1
1919
# aiohttp
2020
# aioredis
2121
attrs==21.2.0
22-
# via aiohttp
22+
# via
23+
# aiohttp
24+
# automat
25+
# twisted
26+
automat==20.2.0
27+
# via twisted
2328
botocore==1.20.106
2429
# via aiobotocore
2530
certifi==2021.10.8
@@ -32,17 +37,24 @@ colorama==0.4.4
3237
# via rich
3338
commonmark==0.9.1
3439
# via rich
40+
constantly==15.1.0
41+
# via twisted
3542
h11==0.12.0
3643
# via httpcore
3744
httpcore==0.13.7
3845
# via httpx
3946
httpx==0.20.0
4047
# via -r requirements.in
48+
hyperlink==21.0.0
49+
# via twisted
4150
idna==3.3
4251
# via
4352
# anyio
53+
# hyperlink
4454
# rfc3986
4555
# yarl
56+
incremental==21.3.0
57+
# via twisted
4658
jmespath==0.10.0
4759
# via botocore
4860
multidict==5.2.0
@@ -58,16 +70,21 @@ rfc3986[idna2008]==1.5.0
5870
rich==10.12.0
5971
# via -r requirements.in
6072
six==1.16.0
61-
# via python-dateutil
73+
# via
74+
# automat
75+
# python-dateutil
6276
sniffio==1.2.0
6377
# via
6478
# anyio
6579
# httpcore
6680
# httpx
81+
twisted==21.7.0
82+
# via -r requirements.in
6783
typing-extensions==3.10.0.2
6884
# via
6985
# aiohttp
7086
# aioredis
87+
# twisted
7188
urllib3==1.26.7
7289
# via botocore
7390
uvloop==0.16.0
@@ -76,3 +93,8 @@ wrapt==1.13.2
7693
# via aiobotocore
7794
yarl==1.7.0
7895
# via aiohttp
96+
zope.interface==5.4.0
97+
# via twisted
98+
99+
# The following packages are considered to be unsafe in a requirements file:
100+
# setuptools

tests/test_inequal_producer_consumer.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ async def test_consumer(*args):
8080
@patch("patterns.inequal_producer_consumer.asyncio.Queue", autospec=True)
8181
@patch("patterns.inequal_producer_consumer.consumer", autospec=True)
8282
@patch("patterns.inequal_producer_consumer.producer", new_callable=Mock())
83-
async def test_main(
83+
async def test_orchestrator(
8484
mock_producer,
8585
mock_consumer,
8686
mock_asyncio_queue,
@@ -90,7 +90,7 @@ async def test_main(
9090
):
9191

9292
# Call the 'main' function.
93-
await main.main()
93+
await main.orchestrator()
9494

9595
mock_asyncio_event.assert_called_once()
9696
mock_asyncio_queue.assert_called_once()

tests/test_twisted_callback_hell.py

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from unittest.mock import patch
2+
3+
import pytest
4+
from twisted.internet import defer, reactor
5+
6+
import patterns.twisted_callback_hell as main
7+
8+
9+
def test_get_dummy_number(capsys):
10+
# Call 'get_dummy_number'.
11+
main.get_dummy_number(input_number=10, defer_duration=0, multiplier=20)
12+
13+
# Assert.
14+
out, err = capsys.readouterr()
15+
assert err == ""
16+
assert "get_dummy_number called" in out
17+
18+
19+
def test_cb_print_number(capsys):
20+
input_number = 11
21+
22+
# Call 'cb_print_number'.
23+
main.cb_print_number(result=input_number)
24+
25+
# Assert.
26+
out, err = capsys.readouterr()
27+
assert err == ""
28+
assert f"Result received: {input_number}" in out
29+
30+
31+
@patch(
32+
"patterns.twisted_callback_hell.get_dummy_number",
33+
autospec=True,
34+
return_value=main.get_dummy_number(
35+
input_number=1,
36+
defer_duration=0.1,
37+
multiplier=2,
38+
),
39+
)
40+
def test_orchestrator(mock_get_dummy_number, capsys):
41+
42+
# Call 'orchestrator'.
43+
# On Python3.10, twisted raises:
44+
# DeprecationWarning: currentThread() is deprecated, use current_thread() instead
45+
with pytest.warns(None):
46+
main.orchestrator(stop_after=0.01)
47+
48+
# Assert.
49+
out, err = capsys.readouterr()
50+
assert err == ""
51+
assert "Result received: 2" in out
52+
mock_get_dummy_number.assert_called_once()

0 commit comments

Comments
 (0)