From 5bace1a3f82546dad416ad80789440900797a12b Mon Sep 17 00:00:00 2001 From: s3lph Date: Fri, 17 Aug 2018 20:44:07 +0200 Subject: [PATCH] Implemented pagelet initialization. --- matemat/webserver/__init__.py | 2 +- matemat/webserver/httpd.py | 41 +++++++++++++++++++- matemat/webserver/pagelets/__init__.py | 1 + matemat/webserver/pagelets/initialization.py | 28 +++++++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 matemat/webserver/pagelets/initialization.py diff --git a/matemat/webserver/__init__.py b/matemat/webserver/__init__.py index bf3f2f8..bd62400 100644 --- a/matemat/webserver/__init__.py +++ b/matemat/webserver/__init__.py @@ -8,5 +8,5 @@ server will attempt to serve the request with a static resource in a previously from .requestargs import RequestArgument, RequestArguments from .responses import PageletResponse, RedirectResponse, TemplateResponse -from .httpd import MatematWebserver, HttpHandler, pagelet +from .httpd import MatematWebserver, HttpHandler, pagelet, pagelet_init from .config import parse_config_file diff --git a/matemat/webserver/httpd.py b/matemat/webserver/httpd.py index 20f5bc8..24916be 100644 --- a/matemat/webserver/httpd.py +++ b/matemat/webserver/httpd.py @@ -1,7 +1,8 @@ -from typing import Any, Callable, Dict, Tuple, Type, Union +from typing import Any, Callable, Dict, List, Tuple, Type, Union import logging +import sys import os import socket import mimetypes @@ -38,6 +39,8 @@ _PAGELET_PATHS: Dict[str, Callable[[str, # HTTP method (GET, POST, ...) bytes, str, # Response body: will assign HTTP/1.0 200 OK PageletResponse, # A generic response ]]] = dict() +# The pagelet initialization functions, to be executed upon startup +_PAGELET_INIT_FUNCTIONS: List[Callable[[Dict[str, str], logging.Logger], None]] = [] # Inactivity timeout for client sessions _SESSION_TIMEOUT: int = 3600 @@ -94,6 +97,28 @@ def pagelet(path: str): return http_handler +def pagelet_init(fun: Callable[[Dict[str, str], logging.Logger], None]): + """ + Annotate a function to act as a pagelet initialization function. The function will be called when the webserver is + started. The function should set up everything required for pagelets to operate correctly, e.g. providing default + values for configuration items, or performing database schema migrations. + + Any exception thrown by an initialization function will cause the web server to seize operation. Multiple + initialization functions can be used. + + The function must have the following signature: + + (config: Dict[str, str], logger: logging.Logger) -> None + + config: The mutable dictionary of variables read from the [Pagelets] section of the configuration file. + logger: The server's logger instance. + returns: Nothing. + + :param fun: The function to annotate + """ + _PAGELET_INIT_FUNCTIONS.append(fun) + + class MatematHTTPServer(HTTPServer): """ A http.server.HTTPServer subclass that acts as a container for data that must be persistent between requests. @@ -184,8 +209,20 @@ class MatematWebserver(object): def start(self) -> None: """ - Start the web server. This call blocks while the server is running. + Call all pagelet initialization functions and start the web server. This call blocks while the server is + running. If any exception is raised in the initialization phase, the program is terminated with a non-zero + exit code. """ + try: + # Run all pagelet initialization functions + for fun in _PAGELET_INIT_FUNCTIONS: + fun(self._httpd.pagelet_variables, self._httpd.logger) + except BaseException as e: + # If an error occurs, log it and terminate + self._httpd.logger.exception(e) + self._httpd.logger.critical('An initialization pagelet raised an error. Stopping.') + sys.exit(1) + # If pagelet initialization went fine, start the HTTP server self._httpd.serve_forever() diff --git a/matemat/webserver/pagelets/__init__.py b/matemat/webserver/pagelets/__init__.py index 4ef6fd5..43b7f31 100644 --- a/matemat/webserver/pagelets/__init__.py +++ b/matemat/webserver/pagelets/__init__.py @@ -4,6 +4,7 @@ This package contains the pagelet functions served by the Matemat software. A new pagelet function must be imported here to be automatically loaded when the server is started. """ +from .initialization import initialization from .main import main_page from .login import login_page from .logout import logout diff --git a/matemat/webserver/pagelets/initialization.py b/matemat/webserver/pagelets/initialization.py new file mode 100644 index 0000000..27636b6 --- /dev/null +++ b/matemat/webserver/pagelets/initialization.py @@ -0,0 +1,28 @@ +from typing import Dict + +import logging + +from matemat.webserver import pagelet_init +from matemat.db import MatematDatabase + + +@pagelet_init +def initialization(config: Dict[str, str], + logger: logging.Logger) -> None: + """ + The pagelet initialization function. Makes sure everything is ready for operation. If anything fails here, the web + server won't resume operation. + """ + # Set default values for missing config items + if 'InstanceName' not in config: + config['InstanceName'] = 'Matemat' + logger.warning('Property \'InstanceName\' not set, using \'Matemat\'') + if 'UploadDir' not in config: + config['UploadDir'] = './static/upload/' + logger.warning('Property \'UploadDir\' not set, using \'./static/upload/\'') + if 'DatabaseFile' not in config: + config['DatabaseFile'] = './matemat.db' + logger.warning('Property \'DatabaseFile\' not set, using \'./matemat.db\'') + with MatematDatabase(config['DatabaseFile']): + # Connect to the database to create it and perform any schema migrations + pass