forked from platypush/platypush
Added use_werkzeug_server
option to backend.http
.
There are situations where you may not want to run the HTTP server in a full blown WSGI-over-Tornado container - unit/integration tests and embedded single-core devices are among those cases. In those scenarios, we should allow the user to be able to run the backend using the built-in Werkzeug server provided by Flask.
This commit is contained in:
parent
013274bcbc
commit
666ea9ea6b
3 changed files with 48 additions and 22 deletions
|
@ -199,6 +199,7 @@ class HttpBackend(Backend):
|
||||||
resource_dirs: Optional[Mapping[str, str]] = None,
|
resource_dirs: Optional[Mapping[str, str]] = None,
|
||||||
secret_key_file: Optional[str] = None,
|
secret_key_file: Optional[str] = None,
|
||||||
num_workers: Optional[int] = None,
|
num_workers: Optional[int] = None,
|
||||||
|
use_werkzeug_server: bool = False,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
|
@ -211,6 +212,16 @@ class HttpBackend(Backend):
|
||||||
:param secret_key_file: Path to the file containing the secret key that will be used by Flask
|
:param secret_key_file: Path to the file containing the secret key that will be used by Flask
|
||||||
(default: ``~/.local/share/platypush/flask.secret.key``).
|
(default: ``~/.local/share/platypush/flask.secret.key``).
|
||||||
:param num_workers: Number of worker processes to use (default: ``(cpu_count * 2) + 1``).
|
:param num_workers: Number of worker processes to use (default: ``(cpu_count * 2) + 1``).
|
||||||
|
:param use_werkzeug_server: Whether the backend should be served by a
|
||||||
|
Werkzeug server (default: ``False``). Note that using the built-in
|
||||||
|
Werkzeug server instead of Tornado is very inefficient, and it
|
||||||
|
doesn't support websocket-based features either so the UI will
|
||||||
|
probably be severely limited. You should only use this option if:
|
||||||
|
|
||||||
|
- You are running tests.
|
||||||
|
- You have issues with running a full Tornado server - for
|
||||||
|
example, you are running the application on a small embedded
|
||||||
|
device that doesn't support Tornado.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
@ -235,6 +246,7 @@ class HttpBackend(Backend):
|
||||||
)
|
)
|
||||||
self.local_base_url = f'http://localhost:{self.port}'
|
self.local_base_url = f'http://localhost:{self.port}'
|
||||||
self.num_workers = num_workers or (cpu_count() * 2) + 1
|
self.num_workers = num_workers or (cpu_count() * 2) + 1
|
||||||
|
self.use_werkzeug_server = use_werkzeug_server
|
||||||
|
|
||||||
def send_message(self, *_, **__):
|
def send_message(self, *_, **__):
|
||||||
self.logger.warning('Use cURL or any HTTP client to query the HTTP backend')
|
self.logger.warning('Use cURL or any HTTP client to query the HTTP backend')
|
||||||
|
@ -339,14 +351,25 @@ class HttpBackend(Backend):
|
||||||
self.num_workers,
|
self.num_workers,
|
||||||
)
|
)
|
||||||
|
|
||||||
sockets = bind_sockets(self.port, address=self.bind_address, reuse_port=True)
|
if self.use_werkzeug_server:
|
||||||
|
application.config['redis_queue'] = self.bus.redis_queue
|
||||||
|
application.run(
|
||||||
|
host=self.bind_address,
|
||||||
|
port=self.port,
|
||||||
|
use_reloader=False,
|
||||||
|
debug=True,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
sockets = bind_sockets(
|
||||||
|
self.port, address=self.bind_address, reuse_port=True
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
fork_processes(self.num_workers)
|
fork_processes(self.num_workers)
|
||||||
future = self._post_fork_main(sockets)
|
future = self._post_fork_main(sockets)
|
||||||
asyncio.run(future)
|
asyncio.run(future)
|
||||||
except (asyncio.CancelledError, KeyboardInterrupt):
|
except (asyncio.CancelledError, KeyboardInterrupt):
|
||||||
return
|
return
|
||||||
|
|
||||||
def _start_web_server(self):
|
def _start_web_server(self):
|
||||||
self._server_proc = Process(target=self._web_server_proc)
|
self._server_proc = Process(target=self._web_server_proc)
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import pytest
|
|
||||||
import time
|
import time
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from platypush import Daemon, Config
|
from platypush import Daemon, Config
|
||||||
|
|
||||||
from .utils import config_file, set_base_url
|
from .utils import config_file, set_base_url
|
||||||
|
@ -17,8 +18,6 @@ def clear_loggers():
|
||||||
This is to prevent pytest spitting out logging errors on teardown if the logging objects have been deinitialized
|
This is to prevent pytest spitting out logging errors on teardown if the logging objects have been deinitialized
|
||||||
(see https://github.com/pytest-dev/pytest/issues/5502#issuecomment-647157873).
|
(see https://github.com/pytest-dev/pytest/issues/5502#issuecomment-647157873).
|
||||||
"""
|
"""
|
||||||
import logging
|
|
||||||
# noinspection PyUnresolvedReferences
|
|
||||||
loggers = [logging.getLogger()] + list(logging.Logger.manager.loggerDict.values())
|
loggers = [logging.getLogger()] + list(logging.Logger.manager.loggerDict.values())
|
||||||
for logger in loggers:
|
for logger in loggers:
|
||||||
handlers = getattr(logger, 'handlers', [])
|
handlers = getattr(logger, 'handlers', [])
|
||||||
|
@ -31,31 +30,34 @@ def app():
|
||||||
logging.info('Starting Platypush test service')
|
logging.info('Starting Platypush test service')
|
||||||
|
|
||||||
Config.init(config_file)
|
Config.init(config_file)
|
||||||
app = Daemon(config_file=config_file, redis_queue='platypush-tests/bus')
|
_app = Daemon(config_file=config_file, redis_queue='platypush-tests/bus')
|
||||||
Thread(target=lambda: app.run()).start()
|
Thread(target=_app.run).start()
|
||||||
logging.info('Sleeping {} seconds while waiting for the daemon to start up'.format(app_start_timeout))
|
logging.info(
|
||||||
|
'Sleeping %d seconds while waiting for the daemon to start up',
|
||||||
|
app_start_timeout,
|
||||||
|
)
|
||||||
time.sleep(app_start_timeout)
|
time.sleep(app_start_timeout)
|
||||||
yield app
|
yield _app
|
||||||
|
|
||||||
logging.info('Stopping Platypush test service')
|
logging.info('Stopping Platypush test service')
|
||||||
app.stop_app()
|
_app.stop_app()
|
||||||
clear_loggers()
|
clear_loggers()
|
||||||
db_file = (Config.get('main.db') or {}).get('engine', '')[len('sqlite:///'):]
|
db = (Config.get('main.db') or {}).get('engine', '')[len('sqlite:///') :]
|
||||||
|
|
||||||
if db_file and os.path.isfile(db_file):
|
if db and os.path.isfile(db):
|
||||||
logging.info('Removing temporary db file {}'.format(db_file))
|
logging.info('Removing temporary db file %s', db)
|
||||||
os.unlink(db_file)
|
os.unlink(db)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='session')
|
@pytest.fixture(scope='session')
|
||||||
def db_file():
|
def db_file():
|
||||||
yield Config.get('main.db')['engine'][len('sqlite:///'):]
|
yield Config.get('main.db')['engine'][len('sqlite:///') :]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='session')
|
@pytest.fixture(scope='session')
|
||||||
def base_url():
|
def base_url():
|
||||||
backends = Config.get_backends()
|
backends = Config.get_backends()
|
||||||
assert 'http' in backends, 'Missing HTTP server configuration'
|
assert 'http' in backends, 'Missing HTTP server configuration'
|
||||||
url = 'http://localhost:{port}'.format(port=backends['http']['port'])
|
url = f'http://localhost:{backends["http"]["port"]}'
|
||||||
set_base_url(url)
|
set_base_url(url)
|
||||||
yield url
|
yield url
|
||||||
|
|
|
@ -6,7 +6,8 @@ main.db:
|
||||||
|
|
||||||
backend.http:
|
backend.http:
|
||||||
port: 8123
|
port: 8123
|
||||||
disable_websocket: True
|
num_workers: 1
|
||||||
|
use_werkzeug_server: True
|
||||||
|
|
||||||
backend.redis:
|
backend.redis:
|
||||||
disabled: False
|
disabled: False
|
||||||
|
|
Loading…
Reference in a new issue