2021-03-01 13:03:26 +00:00
|
|
|
import logging
|
2017-01-28 01:34:21 +00:00
|
|
|
import multiprocessing
|
2019-04-23 22:44:42 +01:00
|
|
|
import pickle
|
2017-01-28 01:34:21 +00:00
|
|
|
import random
|
|
|
|
import signal
|
2022-12-15 09:49:26 +00:00
|
|
|
import sys
|
2019-04-23 22:44:42 +01:00
|
|
|
|
2022-09-18 15:17:23 +01:00
|
|
|
from asyncio import sleep
|
|
|
|
|
2018-09-29 18:54:47 +01:00
|
|
|
import pytest
|
2017-01-08 17:55:08 +00:00
|
|
|
|
2021-01-19 13:54:20 +00:00
|
|
|
from sanic_testing.testing import HOST, PORT
|
|
|
|
|
2022-09-18 15:17:23 +01:00
|
|
|
from sanic import Blueprint, text
|
2022-12-15 09:49:26 +00:00
|
|
|
from sanic.compat import use_context
|
2021-03-01 13:03:26 +00:00
|
|
|
from sanic.log import logger
|
2022-09-18 15:17:23 +01:00
|
|
|
from sanic.server.socket import configure_socket
|
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):
|
2017-01-28 01:34:21 +00:00
|
|
|
"""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))
|
2017-01-28 01:34:21 +00:00
|
|
|
process_list = set()
|
2016-12-26 22:37:05 +00:00
|
|
|
|
2022-09-18 15:17:23 +01:00
|
|
|
@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)
|
2022-09-18 15:17:23 +01:00
|
|
|
|
|
|
|
assert len(process_list) == num_workers + 1
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
|
|
not hasattr(signal, "SIGALRM"),
|
|
|
|
reason="SIGALRM is not implemented for this platform, we have to come "
|
|
|
|
"up with another timeout strategy to test these",
|
|
|
|
)
|
|
|
|
def test_multiprocessing_legacy(app):
|
|
|
|
"""Tests that the number of children we produce is correct"""
|
|
|
|
# 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)
|
|
|
|
app.run(HOST, 4121, workers=num_workers, debug=True, legacy=True)
|
|
|
|
|
|
|
|
assert len(process_list) == num_workers
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
|
|
not hasattr(signal, "SIGALRM"),
|
|
|
|
reason="SIGALRM is not implemented for this platform, we have to come "
|
|
|
|
"up with another timeout strategy to test these",
|
|
|
|
)
|
|
|
|
def test_multiprocessing_legacy_sock(app):
|
|
|
|
"""Tests that the number of children we produce is correct"""
|
|
|
|
# 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()
|
|
|
|
|
2017-01-28 01:34:21 +00:00
|
|
|
def stop_on_alarm(*args):
|
|
|
|
for process in multiprocessing.active_children():
|
|
|
|
process_list.add(process.pid)
|
2016-12-26 22:37:05 +00:00
|
|
|
|
2017-01-28 01:34:21 +00:00
|
|
|
signal.signal(signal.SIGALRM, stop_on_alarm)
|
2022-09-18 15:17:23 +01:00
|
|
|
signal.alarm(2)
|
|
|
|
sock = configure_socket(
|
|
|
|
{
|
|
|
|
"host": HOST,
|
|
|
|
"port": 4121,
|
|
|
|
"unix": None,
|
|
|
|
"backlog": 100,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
app.run(workers=num_workers, debug=True, legacy=True, sock=sock)
|
|
|
|
sock.close()
|
|
|
|
|
|
|
|
assert len(process_list) == num_workers
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
|
|
not hasattr(signal, "SIGALRM"),
|
|
|
|
reason="SIGALRM is not implemented for this platform, we have to come "
|
|
|
|
"up with another timeout strategy to test these",
|
|
|
|
)
|
|
|
|
def test_multiprocessing_legacy_unix(app):
|
|
|
|
"""Tests that the number of children we produce is correct"""
|
|
|
|
# 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)
|
|
|
|
app.run(workers=num_workers, debug=True, legacy=True, unix="./test.sock")
|
2016-12-26 22:37:05 +00:00
|
|
|
|
2017-01-28 01:34:21 +00:00
|
|
|
assert len(process_list) == num_workers
|
2018-11-04 05:04:12 +00:00
|
|
|
|
|
|
|
|
2018-11-10 14:26:55 +00:00
|
|
|
@pytest.mark.skipif(
|
2018-12-30 11:18:06 +00:00
|
|
|
not hasattr(signal, "SIGALRM"),
|
|
|
|
reason="SIGALRM is not implemented for this platform",
|
2018-11-10 14:26:55 +00: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-11-04 05:04:12 +00:00
|
|
|
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()
|
|
|
|
|
2022-09-18 15:17:23 +01:00
|
|
|
@app.after_server_start
|
|
|
|
async def shutdown(app):
|
|
|
|
await sleep(2.1)
|
|
|
|
app.stop()
|
|
|
|
|
2018-11-04 05:04:12 +00:00
|
|
|
def stop_on_alarm(*args):
|
|
|
|
for process in multiprocessing.active_children():
|
|
|
|
process_list.add(process.pid)
|
|
|
|
|
|
|
|
signal.signal(signal.SIGALRM, stop_on_alarm)
|
2022-09-18 15:17:23 +01:00
|
|
|
signal.alarm(2)
|
2018-11-04 05:04:12 +00:00
|
|
|
|
2018-12-30 11:18:06 +00:00
|
|
|
bp = Blueprint("test_text")
|
2018-11-04 05:04:12 +00:00
|
|
|
app.blueprint(bp)
|
2022-12-15 09:49:26 +00:00
|
|
|
with use_context("fork"):
|
|
|
|
app.run(HOST, 4121, workers=num_workers, debug=True)
|
2018-11-04 05:04:12 +00:00
|
|
|
|
2022-09-18 15:17:23 +01:00
|
|
|
assert len(process_list) == num_workers + 1
|
2018-11-04 05:04:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
# 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")
|
|
|
|
|
2018-11-04 05:04:12 +00:00
|
|
|
|
2022-09-18 15:17:23 +01:00
|
|
|
def stop(app):
|
|
|
|
app.stop()
|
|
|
|
|
|
|
|
|
2019-09-18 18:22:24 +01:00
|
|
|
# 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])
|
2018-11-04 05:04:12 +00:00
|
|
|
def test_pickle_app(app, protocol):
|
2018-12-30 11:18:06 +00:00
|
|
|
app.route("/")(handler)
|
2022-09-18 15:17:23 +01:00
|
|
|
app.after_server_start(stop)
|
2021-02-08 12:09:41 +00:00
|
|
|
app.router.reset()
|
2022-09-18 15:17:23 +01:00
|
|
|
app.signal_router.reset()
|
2018-11-04 05:04:12 +00:00
|
|
|
p_app = pickle.dumps(app, protocol=protocol)
|
2019-09-18 18:22:24 +01:00
|
|
|
del app
|
2018-11-04 05:04:12 +00:00
|
|
|
up_p_app = pickle.loads(p_app)
|
|
|
|
assert up_p_app
|
2022-09-18 15:17:23 +01:00
|
|
|
up_p_app.run(single_process=True)
|
2018-11-04 05:04:12 +00:00
|
|
|
|
|
|
|
|
2018-12-30 11:18:06 +00:00
|
|
|
@pytest.mark.parametrize("protocol", [3, 4])
|
2018-11-04 05:27:25 +00:00
|
|
|
def test_pickle_app_with_bp(app, protocol):
|
2018-12-30 11:18:06 +00:00
|
|
|
bp = Blueprint("test_text")
|
|
|
|
bp.route("/")(handler)
|
2022-09-18 15:17:23 +01:00
|
|
|
bp.after_server_start(stop)
|
2018-11-04 05:04:12 +00:00
|
|
|
app.blueprint(bp)
|
2021-02-08 12:09:41 +00:00
|
|
|
app.router.reset()
|
2022-09-18 15:17:23 +01:00
|
|
|
app.signal_router.reset()
|
2018-11-04 05:04:12 +00:00
|
|
|
p_app = pickle.dumps(app, protocol=protocol)
|
2019-09-18 18:22:24 +01:00
|
|
|
del app
|
2018-11-04 05:04:12 +00:00
|
|
|
up_p_app = pickle.loads(p_app)
|
|
|
|
assert up_p_app
|
2022-09-18 15:17:23 +01:00
|
|
|
up_p_app.run(single_process=True)
|
2020-05-07 02:06:10 +01:00
|
|
|
|
2020-06-28 12:45:52 +01:00
|
|
|
|
2020-05-07 02:06:10 +01:00
|
|
|
@pytest.mark.parametrize("protocol", [3, 4])
|
|
|
|
def test_pickle_app_with_static(app, protocol):
|
|
|
|
app.route("/")(handler)
|
2022-09-18 15:17:23 +01:00
|
|
|
app.after_server_start(stop)
|
2020-06-28 12:45:52 +01:00
|
|
|
app.static("/static", "/tmp/static")
|
2021-02-08 12:09:41 +00:00
|
|
|
app.router.reset()
|
2022-09-18 15:17:23 +01:00
|
|
|
app.signal_router.reset()
|
2020-05-07 02:06:10 +01:00
|
|
|
p_app = pickle.dumps(app, protocol=protocol)
|
|
|
|
del app
|
|
|
|
up_p_app = pickle.loads(p_app)
|
|
|
|
assert up_p_app
|
2022-09-18 15:17:23 +01:00
|
|
|
up_p_app.run(single_process=True)
|
2021-03-01 13:03:26 +00:00
|
|
|
|
|
|
|
|
2022-12-15 09:49:26 +00:00
|
|
|
@pytest.mark.skipif(
|
|
|
|
sys.platform not in ("linux", "darwin"),
|
|
|
|
reason="This test requires fork context",
|
|
|
|
)
|
2021-03-01 13:03:26 +00:00
|
|
|
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))
|
|
|
|
|
2022-09-18 15:17:23 +01:00
|
|
|
app.after_server_start(stop)
|
2021-03-01 13:03:26 +00:00
|
|
|
|
|
|
|
@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")
|
|
|
|
|
2021-03-16 09:21:05 +00:00
|
|
|
@app.main_process_start
|
2022-01-16 07:03:04 +00:00
|
|
|
def main_process_start2(app, loop):
|
2021-03-16 09:21:05 +00:00
|
|
|
logger.info("main_process_start")
|
|
|
|
|
|
|
|
@app.main_process_stop
|
2022-01-16 07:03:04 +00:00
|
|
|
def main_process_stop2(app, loop):
|
2021-03-16 09:21:05 +00:00
|
|
|
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)
|
2021-03-01 13:03:26 +00:00
|
|
|
|
|
|
|
assert (
|
|
|
|
caplog.record_tuples.count(("sanic.root", 20, "main_process_start"))
|
2021-03-16 09:21:05 +00:00
|
|
|
== 2
|
2021-03-01 13:03:26 +00:00
|
|
|
)
|
|
|
|
assert (
|
|
|
|
caplog.record_tuples.count(("sanic.root", 20, "main_process_stop"))
|
2021-03-16 09:21:05 +00:00
|
|
|
== 2
|
2021-03-01 13:03:26 +00:00
|
|
|
)
|