diff options
Diffstat (limited to '')
| -rw-r--r-- | .snekrc | 19 | ||||
| -rw-r--r-- | app.py | 110 | ||||
| -rw-r--r-- | error_handlers/http_404.py | 8 | ||||
| -rw-r--r-- | pysite/__init__.py | 3 | ||||
| -rw-r--r-- | pysite/base_route.py | 29 | ||||
| -rw-r--r-- | pysite/route_manager.py | 41 | ||||
| -rw-r--r-- | pysite/views/__init__.py | 3 | ||||
| -rw-r--r-- | pysite/views/error_handlers/__init__.py | 3 | ||||
| -rw-r--r-- | pysite/views/error_handlers/http_404.py | 14 | ||||
| -rw-r--r-- | pysite/views/healthcheck.py | 15 | ||||
| -rw-r--r-- | pysite/views/index.py | 12 | ||||
| -rw-r--r-- | pysite/views/invite.py | 15 | ||||
| -rw-r--r-- | requirements.txt | 2 | ||||
| -rw-r--r-- | routes/healthcheck.py | 8 | ||||
| -rw-r--r-- | routes/index.py | 8 | ||||
| -rw-r--r-- | routes/invite.py | 9 | ||||
| -rw-r--r-- | templates/.gitkeep | 0 | 
17 files changed, 144 insertions, 155 deletions
| @@ -1,21 +1,6 @@  [all] -linters = flake8, safety, dodgy, yapf, isort +linters = flake8, safety, dodgy  [flake8]  max-line-length=120 -application_import_names=pysite -ignore=I100 - -[isort] -line_length=120 -indent='    ' -multi_line_output=0 -length_sort=1 -use_parentheses=true -sections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER -import_heading_stdlib=Stdlib -import_heading_thirdparty=External Libraries -import_heading_firstparty=Site Internals -force_sort_within_sections=true - -[style]  # yapf
\ No newline at end of file +application_import_names=pysite
\ No newline at end of file @@ -1,108 +1,10 @@ -#!/usr/bin/env python3.6 +# coding=utf-8 -# Stdlib -from importlib.util import module_from_spec, spec_from_file_location -import mimetypes -import os +from pysite.route_manager import RouteManager -# External Libraries -from japronto import Application -app = Application() +manager = RouteManager() +app = manager.app - -def static_file(path: str):  # type: (str) -> (req: {Response}) -> Coroutine -    async def inner(req):  # type: ({Response}) -> Coroutine -        with open(path) as file: -            return req.Response( -                text=file.read(), mime_type=mimetypes.guess_type(path)[0]) - -    return inner - - -def find_static_files(dir_: str) -> list:  # type: (str) -> List[str] -    data = [] -    for path, _, files in os.walk(dir_): -        if not files: -            continue - -        for file in files: -            if not file.split(".")[-1] not in ("html", "css", "js"): -                continue - -            pathname = f"{path[len(dir_):]}/{file.strip()}" -            data.append(pathname.split(".")[0], static_file(pathname)) - -    return data - - -def find_routes( -        dir_: str) -> list:  # type: (str) -> List[Tuple[str, str, callable]] -    data = [] -    for path, _, files in os.walk(dir_): -        if not files: -            continue - -        for file in files: -            if not file.endswith(".py"): -                continue -            pathname = f"{path}/{file.strip()}" -            spec = spec_from_file_location(file[:-3], pathname) -            module = module_from_spec(spec) -            spec.loader.exec_module(module) - -            if not hasattr(module, "Index"): -                raise Exception("No `Index` class!") - -            res = module.Index() -            del module, spec - -            route_paths = res.path -            for path_ in route_paths: -                # TODO: Add all request types here -                for method in ("GET", "POST", "DELETE", "PATCH"): -                    if hasattr(res, method.lower()): -                        data.append((path_, method, getattr( -                            res, method.lower()))) - -    return data - - -def find_errors(dir_: str) -> list:  # type: (str) -> List[str] -    data = [] -    for path, _, files in os.walk(dir_): -        if not files: -            continue - -        for file in files: -            if not file.endswith(".py"): -                continue -            pathname = f"{path}/{file.strip()}" -            spec = spec_from_file_location(file[:-3], pathname) -            module = module_from_spec(spec) -            spec.loader.exec_module(module) - -            if not hasattr(module, "Index"): -                raise Exception("No `Index` class!") - -            res = module.Index() -            del module, spec -            data.append((res.error_code, res.err)) - -    return data - - -routes = find_routes("routes") -static = find_static_files("static") -errors = find_errors("error_handlers") - -for path, method, handle in routes: -    app.router.add_route(path, handle, method=method) - -for path, handle in static: -    app.router.add_route(path, handle, method="GET") - -for errcode, handler in errors: -    app.add_error_handler(errcode, handler) - -app.run(debug=True, port=80) +if __name__ == '__main__': +    manager.run() diff --git a/error_handlers/http_404.py b/error_handlers/http_404.py deleted file mode 100644 index 712bf748..00000000 --- a/error_handlers/http_404.py +++ /dev/null @@ -1,8 +0,0 @@ -# coding=utf-8 - - -class Index: -    error_code = 404 - -    def err(self, req): -        return req.Response(text="Page not found!", code=404) diff --git a/pysite/__init__.py b/pysite/__init__.py new file mode 100644 index 00000000..ba286add --- /dev/null +++ b/pysite/__init__.py @@ -0,0 +1,3 @@ +# coding=utf-8 + +__author__ = "Gareth Coles" diff --git a/pysite/base_route.py b/pysite/base_route.py new file mode 100644 index 00000000..76338280 --- /dev/null +++ b/pysite/base_route.py @@ -0,0 +1,29 @@ +# coding=utf-8 +from flask import Flask +from flask.views import MethodView + +__author__ = "Gareth Coles" + + +class BaseView(MethodView): +    path = None  #: str +    name = None  #: str + +    @classmethod +    def setup(cls: "BaseView", app: Flask): +        if not cls.path or not cls.name: +            raise RuntimeError("Route views must have both `path` and `name` defined") + +        app.add_url_rule(cls.path, view_func=cls.as_view(cls.name)) + + +class ErrorView(MethodView): +    name = None  #: str +    error_code = None  #: int + +    @classmethod +    def setup(cls: "ErrorView", app: Flask): +        if not cls.name or not cls.error_code: +            raise RuntimeError("Error views must have both `name` and `error_code` defined") + +        app._register_error_handler(None, 404, cls.as_view(cls.name)) diff --git a/pysite/route_manager.py b/pysite/route_manager.py new file mode 100644 index 00000000..501076b7 --- /dev/null +++ b/pysite/route_manager.py @@ -0,0 +1,41 @@ +# coding=utf-8 +import importlib +import inspect +import os + +from flask import Flask + +from pysite.base_route import BaseView, ErrorView + +__author__ = "Gareth Coles" + + +class RouteManager: +    def __init__(self): +        self.app = Flask(__name__) +        self.app.secret_key = os.environ.get("WEBPAGE_SECRET_KEY") + +        self.load_views() + +    def run(self): +        self.app.run(port=int(os.environ.get("WEBPAGE_PORT")), debug=False) + +    def load_views(self, location="pysite/views"): +        for filename in os.listdir(location): +            if os.path.isdir(f"{location}/{filename}"): +                # Recurse if it's a directory; load ALL the views! +                self.load_views(location=f"{location}/{filename}") +                continue + +            if filename.endswith(".py") and not filename.startswith("__init__"): +                module = importlib.import_module(f"{location}/{filename}".replace("/", ".")[:-3]) + +                for cls_name, cls in inspect.getmembers(module): +                    if ( +                            inspect.isclass(cls) and +                            cls is not BaseView and +                            cls is not ErrorView and +                            (BaseView in cls.__mro__ or ErrorView in cls.__mro__) +                    ): +                        cls.setup(self.app) +                        print(f"View loaded: {cls.name: <25} ({module.__name__}.{cls_name})") diff --git a/pysite/views/__init__.py b/pysite/views/__init__.py new file mode 100644 index 00000000..ba286add --- /dev/null +++ b/pysite/views/__init__.py @@ -0,0 +1,3 @@ +# coding=utf-8 + +__author__ = "Gareth Coles" diff --git a/pysite/views/error_handlers/__init__.py b/pysite/views/error_handlers/__init__.py new file mode 100644 index 00000000..ba286add --- /dev/null +++ b/pysite/views/error_handlers/__init__.py @@ -0,0 +1,3 @@ +# coding=utf-8 + +__author__ = "Gareth Coles" diff --git a/pysite/views/error_handlers/http_404.py b/pysite/views/error_handlers/http_404.py new file mode 100644 index 00000000..eea1e630 --- /dev/null +++ b/pysite/views/error_handlers/http_404.py @@ -0,0 +1,14 @@ +# coding=utf-8 +from werkzeug.exceptions import NotFound + +from pysite.base_route import ErrorView + +__author__ = "Gareth Coles" + + +class Error404View(ErrorView): +    name = "error_404" +    error_code = 404 + +    def get(self, error: NotFound): +        return "replace me with a template, 404 not found", 404 diff --git a/pysite/views/healthcheck.py b/pysite/views/healthcheck.py new file mode 100644 index 00000000..660c8a96 --- /dev/null +++ b/pysite/views/healthcheck.py @@ -0,0 +1,15 @@ +# coding=utf-8 +from flask import jsonify + +from pysite.base_route import BaseView + + +__author__ = "Gareth Coles" + + +class IndexView(BaseView): +    path = "/healthcheck" +    name = "healthcheck" + +    def get(self): +        return jsonify({"status": "ok"}) diff --git a/pysite/views/index.py b/pysite/views/index.py new file mode 100644 index 00000000..2e779003 --- /dev/null +++ b/pysite/views/index.py @@ -0,0 +1,12 @@ +# coding=utf-8 +from pysite.base_route import BaseView + +__author__ = "Gareth Coles" + + +class IndexView(BaseView): +    path = "/" +    name = "index" + +    def get(self): +        return "Coming soon:tm:" diff --git a/pysite/views/invite.py b/pysite/views/invite.py new file mode 100644 index 00000000..d035fc99 --- /dev/null +++ b/pysite/views/invite.py @@ -0,0 +1,15 @@ +# coding=utf-8 +from flask import redirect + +from pysite.base_route import BaseView + + +__author__ = "Gareth Coles" + + +class InviteView(BaseView): +    path = "/invite" +    name = "invite" + +    def get(self): +        return redirect("http://invite.pythondiscord.com/") diff --git a/requirements.txt b/requirements.txt index 8f1f53e4..69ca547f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -japronto +flask==0.12.2
\ No newline at end of file diff --git a/routes/healthcheck.py b/routes/healthcheck.py deleted file mode 100644 index 11a5fc24..00000000 --- a/routes/healthcheck.py +++ /dev/null @@ -1,8 +0,0 @@ -# coding=utf-8 - - -class Index: -    path = ["/healthcheck"] - -    def get(self, req): -        return req.Response(json={"status": "ok"}) diff --git a/routes/index.py b/routes/index.py deleted file mode 100644 index 76671ed1..00000000 --- a/routes/index.py +++ /dev/null @@ -1,8 +0,0 @@ -# coding=utf-8 - - -class Index: -    path = ["/", "/index"] - -    def get(self, req): -        return req.Response("Coming soon:tm:") diff --git a/routes/invite.py b/routes/invite.py deleted file mode 100644 index 29553345..00000000 --- a/routes/invite.py +++ /dev/null @@ -1,9 +0,0 @@ -# coding=utf-8 - - -class Index: -    path = ["/invite"] - -    def get(self, res): -        return res.Response( -            status_code=301, text="http://invite.pythondiscord.com/") diff --git a/templates/.gitkeep b/templates/.gitkeep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/templates/.gitkeep | 
