Module 15Watt_Wsgi.Kernel

Expand source code
import traceback
from importlib import import_module
from sqlobject import *
from .Request import Request
from .Response import Response
from .Route import HttpMethods
from .Exceptions import Base


class Kernel(object):
        """
                Handles the complete request to response cycle.
        """
        def __init__(self, nameConfig: str = 'config', nameRoutes: str = 'routes'):
                """
                Sets the names of the configuration and routes files.

                These files are expected to be in the project root.

                :param nameConfig: str

                :param nameRoutes: str
                """

                self.__nameConfig = nameConfig
                self.__nameRoutes = nameRoutes

                self.__routes = {}
                self.__config = {}
                self.__env    = {}
                self.__loadConfig()
                self.__connectToDatabase()
                self.__loadRoutes()


        def run(self, env: dict, startResponse):
                """
                        Handles to whole request.

                        Will be called by your application.py
                """
                # Only needed for the __str__ method and debug purposes. DO NOT USE!
                self.__env = env

                # Iterate over all routes
                for idx in self.__routes:
                        if not self.__routes[idx].match(path=env.get('PATH_INFO'), httpMethod=HttpMethods[env.get('REQUEST_METHOD')]):
                                continue

                        # Found, so I call the controller method
                        request = Request(
                                env=env,
                                paramsFromRoute=self.__routes[idx].getParamsFromPath(path=env.get('PATH_INFO'))
                        )

                        response = Response(
                                startResponse=startResponse,
                                request=request
                        )

                        response.addHeader('X-Framework', 'Wsgi by Thomas Siemion')
                        self.__addAccessControlHeader(request=request, response=response)

                        # Call the controller method
                        try:
                                self.__routes[idx].methodToCall(
                                        request=request,
                                        response=response
                                )

                        except Exception as e:
                                if 'debug' in self.__config and self.__config['debug'] is True:
                                        tb = traceback.format_exc()
                                else:
                                        tb = ''

                                if issubclass(type(e), Base):
                                        response.returnCode    = e.returnCode
                                        response.stringContent = e.returnMsg
                                else:
                                        response.returnCode    = 500
                                        response.stringContent = f'Internal server _error.\n{tb}'

                                response.contentType = 'text/plain'
                                response.charset     = 'utf-8'

                        return response.getContent()

                # No matching route found
                # todo create a Error-Controller
                response = Response(
                        startResponse=startResponse,
                        request=Request(env=env, paramsFromRoute={})
                )

                response.returnCode    = 404
                response.stringContent = f"No controller method found for route: {env.get('REQUEST_METHOD')}_{env.get('PATH_INFO')}"
                response.contentType   = 'text/plain'
                response.charset       = 'utf-8'

                return response.getContent()


        def __loadConfig(self):
                """
                        Reads the variables from project root config.py
                :return:
                """
                config = import_module(name=self.__nameConfig)
                for k in dir(config):
                        if k.startswith('__'):
                                continue

                        self.__config[k] = getattr(config, k)

                return


        def __connectToDatabase(self):
                """
                        If the key uriDb is present in the project root config.py, a SqlObject
                        database connection will be established.
                :return:
                """
                if 'uriDb' not in self.__config:
                        self.__config['dbConnection'] = None
                        return

                self.__config['dbConnection'] = connectionForURI(uri=self.__config['uriDb'])
                sqlhub.processConnection = self.__config['dbConnection']


        def __loadRoutes(self):
                """
                        Loads and creates all routes from project root routes.py
                        and injects the configuration to them.
                :return:
                """
                module = import_module(name=self.__nameRoutes)
                for route in getattr(module, self.__nameRoutes):
                        route.setConfig(config=self.__config)
                        k = f'{route.httpMethod.name}_{route.pathRegEx}'
                        self.__routes[k] = route


        def __addAccessControlHeader(self, request: Request, response: Response):
                """
                        Adds the Access-Control-Allow-Origin header to the response,
                        if project root config.py has a key accessControlAllowOrigin,
                        holding an list of strings.

                :param request:
                :param response:
                :return:
                """
                if 'accessControlAllowOrigin' not in self.__config:
                        return

                accessControlAllowOrigin = []
                accessControlAllowOrigin = accessControlAllowOrigin + self.__config['accessControlAllowOrigin']

                if 0 == len(accessControlAllowOrigin) or False == request.hasHeader('Origin'):
                        return

                for url in accessControlAllowOrigin:
                        if request.getHeader('Origin') == url:
                                response.addHeader('Access-Control-Allow-Origin', url)


        def __str__(self):
                """
                        Just a dump string representation of the kernel
                        for debugging purposes only.
                """
                ret = 'Config:\n'
                for k in self.__config:
                        ret += '\t{key}={val}\n'.format(key=k, val=self.__config[k])

                ret += '\nRoutes:\n'

                for k in self.__routes:
                        ret += '\t' + str(self.__routes[k]) + '\n'

                ret += '\nENV:\n'

                for k in self.__env:
                        ret += '\t{key}={val}\n'.format(key=k, val=self.__env[k])

                return ret

Classes

class Kernel (nameConfig: str = 'config', nameRoutes: str = 'routes')

Handles the complete request to response cycle.

Sets the names of the configuration and routes files.

These files are expected to be in the project root.

:param nameConfig: str

:param nameRoutes: str

Expand source code
class Kernel(object):
        """
                Handles the complete request to response cycle.
        """
        def __init__(self, nameConfig: str = 'config', nameRoutes: str = 'routes'):
                """
                Sets the names of the configuration and routes files.

                These files are expected to be in the project root.

                :param nameConfig: str

                :param nameRoutes: str
                """

                self.__nameConfig = nameConfig
                self.__nameRoutes = nameRoutes

                self.__routes = {}
                self.__config = {}
                self.__env    = {}
                self.__loadConfig()
                self.__connectToDatabase()
                self.__loadRoutes()


        def run(self, env: dict, startResponse):
                """
                        Handles to whole request.

                        Will be called by your application.py
                """
                # Only needed for the __str__ method and debug purposes. DO NOT USE!
                self.__env = env

                # Iterate over all routes
                for idx in self.__routes:
                        if not self.__routes[idx].match(path=env.get('PATH_INFO'), httpMethod=HttpMethods[env.get('REQUEST_METHOD')]):
                                continue

                        # Found, so I call the controller method
                        request = Request(
                                env=env,
                                paramsFromRoute=self.__routes[idx].getParamsFromPath(path=env.get('PATH_INFO'))
                        )

                        response = Response(
                                startResponse=startResponse,
                                request=request
                        )

                        response.addHeader('X-Framework', 'Wsgi by Thomas Siemion')
                        self.__addAccessControlHeader(request=request, response=response)

                        # Call the controller method
                        try:
                                self.__routes[idx].methodToCall(
                                        request=request,
                                        response=response
                                )

                        except Exception as e:
                                if 'debug' in self.__config and self.__config['debug'] is True:
                                        tb = traceback.format_exc()
                                else:
                                        tb = ''

                                if issubclass(type(e), Base):
                                        response.returnCode    = e.returnCode
                                        response.stringContent = e.returnMsg
                                else:
                                        response.returnCode    = 500
                                        response.stringContent = f'Internal server _error.\n{tb}'

                                response.contentType = 'text/plain'
                                response.charset     = 'utf-8'

                        return response.getContent()

                # No matching route found
                # todo create a Error-Controller
                response = Response(
                        startResponse=startResponse,
                        request=Request(env=env, paramsFromRoute={})
                )

                response.returnCode    = 404
                response.stringContent = f"No controller method found for route: {env.get('REQUEST_METHOD')}_{env.get('PATH_INFO')}"
                response.contentType   = 'text/plain'
                response.charset       = 'utf-8'

                return response.getContent()


        def __loadConfig(self):
                """
                        Reads the variables from project root config.py
                :return:
                """
                config = import_module(name=self.__nameConfig)
                for k in dir(config):
                        if k.startswith('__'):
                                continue

                        self.__config[k] = getattr(config, k)

                return


        def __connectToDatabase(self):
                """
                        If the key uriDb is present in the project root config.py, a SqlObject
                        database connection will be established.
                :return:
                """
                if 'uriDb' not in self.__config:
                        self.__config['dbConnection'] = None
                        return

                self.__config['dbConnection'] = connectionForURI(uri=self.__config['uriDb'])
                sqlhub.processConnection = self.__config['dbConnection']


        def __loadRoutes(self):
                """
                        Loads and creates all routes from project root routes.py
                        and injects the configuration to them.
                :return:
                """
                module = import_module(name=self.__nameRoutes)
                for route in getattr(module, self.__nameRoutes):
                        route.setConfig(config=self.__config)
                        k = f'{route.httpMethod.name}_{route.pathRegEx}'
                        self.__routes[k] = route


        def __addAccessControlHeader(self, request: Request, response: Response):
                """
                        Adds the Access-Control-Allow-Origin header to the response,
                        if project root config.py has a key accessControlAllowOrigin,
                        holding an list of strings.

                :param request:
                :param response:
                :return:
                """
                if 'accessControlAllowOrigin' not in self.__config:
                        return

                accessControlAllowOrigin = []
                accessControlAllowOrigin = accessControlAllowOrigin + self.__config['accessControlAllowOrigin']

                if 0 == len(accessControlAllowOrigin) or False == request.hasHeader('Origin'):
                        return

                for url in accessControlAllowOrigin:
                        if request.getHeader('Origin') == url:
                                response.addHeader('Access-Control-Allow-Origin', url)


        def __str__(self):
                """
                        Just a dump string representation of the kernel
                        for debugging purposes only.
                """
                ret = 'Config:\n'
                for k in self.__config:
                        ret += '\t{key}={val}\n'.format(key=k, val=self.__config[k])

                ret += '\nRoutes:\n'

                for k in self.__routes:
                        ret += '\t' + str(self.__routes[k]) + '\n'

                ret += '\nENV:\n'

                for k in self.__env:
                        ret += '\t{key}={val}\n'.format(key=k, val=self.__env[k])

                return ret

Methods

def run(self, env: dict, startResponse)

Handles to whole request.

Will be called by your application.py

Expand source code
def run(self, env: dict, startResponse):
        """
                Handles to whole request.

                Will be called by your application.py
        """
        # Only needed for the __str__ method and debug purposes. DO NOT USE!
        self.__env = env

        # Iterate over all routes
        for idx in self.__routes:
                if not self.__routes[idx].match(path=env.get('PATH_INFO'), httpMethod=HttpMethods[env.get('REQUEST_METHOD')]):
                        continue

                # Found, so I call the controller method
                request = Request(
                        env=env,
                        paramsFromRoute=self.__routes[idx].getParamsFromPath(path=env.get('PATH_INFO'))
                )

                response = Response(
                        startResponse=startResponse,
                        request=request
                )

                response.addHeader('X-Framework', 'Wsgi by Thomas Siemion')
                self.__addAccessControlHeader(request=request, response=response)

                # Call the controller method
                try:
                        self.__routes[idx].methodToCall(
                                request=request,
                                response=response
                        )

                except Exception as e:
                        if 'debug' in self.__config and self.__config['debug'] is True:
                                tb = traceback.format_exc()
                        else:
                                tb = ''

                        if issubclass(type(e), Base):
                                response.returnCode    = e.returnCode
                                response.stringContent = e.returnMsg
                        else:
                                response.returnCode    = 500
                                response.stringContent = f'Internal server _error.\n{tb}'

                        response.contentType = 'text/plain'
                        response.charset     = 'utf-8'

                return response.getContent()

        # No matching route found
        # todo create a Error-Controller
        response = Response(
                startResponse=startResponse,
                request=Request(env=env, paramsFromRoute={})
        )

        response.returnCode    = 404
        response.stringContent = f"No controller method found for route: {env.get('REQUEST_METHOD')}_{env.get('PATH_INFO')}"
        response.contentType   = 'text/plain'
        response.charset       = 'utf-8'

        return response.getContent()