sanic/tests/test_multiprocessing.py

175 lines
4.7 KiB
Python
Raw Permalink Normal View History

import logging
import multiprocessing
import pickle
import random
import signal
2022-12-15 09:49:26 +00:00
import sys
from asyncio import sleep
2018-09-29 18:54:47 +01:00
import pytest
2021-01-19 13:54:20 +00:00
from sanic_testing.testing import HOST, PORT
from sanic import Blueprint, text
2022-12-15 09:49:26 +00:00
from sanic.compat import use_context
from sanic.log import logger
2016-10-18 09:22:49 +01:00
2018-09-29 18:54:47 +01:00
@pytest.mark.skipif(
2018-12-30 11:18:06 +00:00
not hasattr(signal, "SIGALRM"),
reason="SIGALRM is not implemented for this platform, we have to come "
"up with another timeout strategy to test these",
2018-09-29 18:54:47 +01:00
)
2022-12-15 09:49:26 +00:00
@pytest.mark.skipif(
sys.platform not in ("linux", "darwin"),
reason="This test requires fork context",
)
2018-08-26 15:43:14 +01:00
def test_multiprocessing(app):
"""Tests that the number of children we produce is correct"""
# Selects a number at random so we can spot check
2018-09-29 18:54:47 +01:00
num_workers = random.choice(range(2, multiprocessing.cpu_count() * 2 + 1))
process_list = set()
@app.after_server_start
async def shutdown(app):
await sleep(2.1)
app.stop()
def stop_on_alarm(*args):
for process in multiprocessing.active_children():
process_list.add(process.pid)
signal.signal(signal.SIGALRM, stop_on_alarm)
signal.alarm(2)
2022-12-15 09:49:26 +00:00
with use_context("fork"):
app.run(HOST, 4120, workers=num_workers, debug=True)
assert len(process_list) == num_workers + 1
@pytest.mark.skipif(
2018-12-30 11:18:06 +00:00
not hasattr(signal, "SIGALRM"),
reason="SIGALRM is not implemented for this platform",
)
2022-12-15 09:49:26 +00:00
@pytest.mark.skipif(
sys.platform not in ("linux", "darwin"),
reason="This test requires fork context",
)
def test_multiprocessing_with_blueprint(app):
# Selects a number at random so we can spot check
num_workers = random.choice(range(2, multiprocessing.cpu_count() * 2 + 1))
process_list = set()
@app.after_server_start
async def shutdown(app):
await sleep(2.1)
app.stop()
def stop_on_alarm(*args):
for process in multiprocessing.active_children():
process_list.add(process.pid)
signal.signal(signal.SIGALRM, stop_on_alarm)
signal.alarm(2)
2018-12-30 11:18:06 +00:00
bp = Blueprint("test_text")
app.blueprint(bp)
2022-12-15 09:49:26 +00:00
with use_context("fork"):
app.run(HOST, 4121, workers=num_workers, debug=True)
assert len(process_list) == num_workers + 1
# this function must be outside a test function so that it can be
# able to be pickled (local functions cannot be pickled).
def handler(request):
2018-12-30 11:18:06 +00:00
return text("Hello")
def stop(app):
app.stop()
# Multiprocessing on Windows requires app to be able to be pickled
2018-12-30 11:18:06 +00:00
@pytest.mark.parametrize("protocol", [3, 4])
def test_pickle_app(app, protocol):
2018-12-30 11:18:06 +00:00
app.route("/")(handler)
app.after_server_start(stop)
2021-02-08 12:09:41 +00:00
app.router.reset()
app.signal_router.reset()
p_app = pickle.dumps(app, protocol=protocol)
del app
up_p_app = pickle.loads(p_app)
assert up_p_app
up_p_app.run(single_process=True)
2018-12-30 11:18:06 +00:00
@pytest.mark.parametrize("protocol", [3, 4])
def test_pickle_app_with_bp(app, protocol):
2018-12-30 11:18:06 +00:00
bp = Blueprint("test_text")
bp.route("/")(handler)
bp.after_server_start(stop)
app.blueprint(bp)
2021-02-08 12:09:41 +00:00
app.router.reset()
app.signal_router.reset()
p_app = pickle.dumps(app, protocol=protocol)
del app
up_p_app = pickle.loads(p_app)
assert up_p_app
up_p_app.run(single_process=True)
@pytest.mark.parametrize("protocol", [3, 4])
def test_pickle_app_with_static(app, protocol):
app.route("/")(handler)
app.after_server_start(stop)
app.static("/static", "/tmp/static")
2021-02-08 12:09:41 +00:00
app.router.reset()
app.signal_router.reset()
p_app = pickle.dumps(app, protocol=protocol)
del app
up_p_app = pickle.loads(p_app)
assert up_p_app
up_p_app.run(single_process=True)
2022-12-15 09:49:26 +00:00
@pytest.mark.skipif(
sys.platform not in ("linux", "darwin"),
reason="This test requires fork context",
)
def test_main_process_event(app, caplog):
# Selects a number at random so we can spot check
num_workers = random.choice(range(2, multiprocessing.cpu_count() * 2 + 1))
app.after_server_start(stop)
@app.listener("main_process_start")
def main_process_start(app, loop):
logger.info("main_process_start")
@app.listener("main_process_stop")
def main_process_stop(app, loop):
logger.info("main_process_stop")
@app.main_process_start
2022-01-16 07:03:04 +00:00
def main_process_start2(app, loop):
logger.info("main_process_start")
@app.main_process_stop
2022-01-16 07:03:04 +00:00
def main_process_stop2(app, loop):
logger.info("main_process_stop")
2022-12-15 09:49:26 +00:00
with use_context("fork"):
with caplog.at_level(logging.INFO):
app.run(HOST, PORT, workers=num_workers)
assert (
caplog.record_tuples.count(("sanic.root", 20, "main_process_start"))
== 2
)
assert (
caplog.record_tuples.count(("sanic.root", 20, "main_process_stop"))
== 2
)