Source code for hookee.server

__copyright__ = "Copyright (c) 2020-2024 Alex Laird"
__license__ = "MIT"

import logging
import threading
import time
from http import HTTPStatus
from urllib.error import URLError
from urllib.request import Request, urlopen

from flask import Flask

from hookee import pluginmanager

werkzeug_logger = logging.getLogger("werkzeug")
werkzeug_logger.setLevel(logging.ERROR)


[docs] class Server: """ An object that manages a non-blocking Flask server and thread. :var hookee_manager: Reference to the ``hookee`` Manager. :vartype hookee_manager: HookeeManager :var plugin_manager: Reference to the Plugin Manager. :vartype plugin_manager: PluginManager :var print_util: Reference to the PrintUtil. :vartype print_util: PrintUtil :var port: The server's port. :vartype port: int :var app: The Flask app. :vartype app: flask.Flask """ def __init__(self, hookee_manager): self.hookee_manager = hookee_manager self.plugin_manager = self.hookee_manager.plugin_manager self.print_util = self.hookee_manager.print_util self.port = self.hookee_manager.config.get("port") self.app = self.create_app() self._thread = None
[docs] def create_app(self): """ Create a Flask app and register all Blueprints found in enabled plugins. :return: The Flask app. :rtype: flask.Flask """ app = Flask(__name__) app.config.from_mapping( ENV="development" ) for plugin in self.plugin_manager.get_plugins_by_type(pluginmanager.BLUEPRINT_PLUGIN): app.register_blueprint(plugin.blueprint) return app
def _loop(self): thread = None try: thread = threading.current_thread() thread.alive = True # This will block until stop() is invoked to shutdown the Werkzeug server self.app.run(host="127.0.0.1", port=self.port, debug=True, use_reloader=False) except OSError as e: self.print_util.print_basic(e) self.stop() if thread: thread.alive = False
[docs] def start(self): """ If one is not already running, start a server in a new thread. """ if self._thread is None: self.print_util.print_open_header("Starting Server") self._thread = threading.Thread(target=self._loop, daemon=True) self._thread.start() while self._server_status() != HTTPStatus.OK: time.sleep(1) self.print_close_header()
[docs] def stop(self): """ This method is only useful when the Flask version has been overriden to use asn older version (<2). With recent versions of Flask, the underlying server daemon will terminate when ``hookee` terminates. """ if self._thread: req = Request(f"http://127.0.0.1:{self.port}/shutdown", method="POST") urlopen(req) self._thread = None
[docs] def _server_status(self): """ Get the response code of the server's ``/status`` endpoint. :return: The status code. :rtype: http.HTTPStatus """ try: return urlopen(f"http://127.0.0.1:{self.port}/status").getcode() except URLError: return HTTPStatus.INTERNAL_SERVER_ERROR
def print_close_header(self): self.print_util.print_basic(f" * Port: {self.port}") self.print_util.print_basic(" * Blueprints: registered") self.print_util.print_close_header()