From 1fed2a2cdcacd3bfcc0b69f7b1a1bb536c8e5268 Mon Sep 17 00:00:00 2001 From: Gareth Coles Date: Tue, 6 Feb 2018 01:01:59 +0000 Subject: Revert "Finally fix this garbage" This reverts commit 57abb43 --- app.py | 111 ++------------------------------ pysite/__init__.py | 3 + pysite/base_route.py | 29 +++++++++ pysite/route_manager.py | 41 ++++++++++++ pysite/views/__init__.py | 3 + pysite/views/error_handlers/__init__.py | 3 + pysite/views/error_handlers/http_404.py | 14 ++++ pysite/views/healthcheck.py | 15 +++++ pysite/views/index.py | 12 ++++ pysite/views/invite.py | 15 +++++ requirements-ci.txt | 2 - requirements.txt | 4 +- templates/.gitkeep | 0 13 files changed, 142 insertions(+), 110 deletions(-) create mode 100644 pysite/__init__.py create mode 100644 pysite/base_route.py create mode 100644 pysite/route_manager.py create mode 100644 pysite/views/__init__.py create mode 100644 pysite/views/error_handlers/__init__.py create mode 100644 pysite/views/error_handlers/http_404.py create mode 100644 pysite/views/healthcheck.py create mode 100644 pysite/views/index.py create mode 100644 pysite/views/invite.py create mode 100644 templates/.gitkeep diff --git a/app.py b/app.py index ea101801..f3bb9a60 100644 --- a/app.py +++ b/app.py @@ -1,109 +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 flask import Flask -from werkzeug.wrappers import Response -app = Flask() +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 Response( - response=file.read(), mimetype=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.add_url_rule(path, path, handle, method=method) - -for path, handle in static: - app.add_url_rule(path, path, handle, method="GET") - -for errcode, handler in errors: - app.register_error_handler(errcode, handler) - -app.run(port=80) +if __name__ == '__main__': + manager.run() 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..17e9aee3 --- /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, cls.error_code, 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-ci.txt b/requirements-ci.txt index ddefba34..04e3aabe 100644 --- a/requirements-ci.txt +++ b/requirements-ci.txt @@ -7,5 +7,3 @@ flake8-tidy-imports flake8-string-format safety dodgy -isort -yapf diff --git a/requirements.txt b/requirements.txt index 64f1a8bf..69ca547f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1 @@ -werkzeug -flask -ujson \ No newline at end of file +flask==0.12.2 \ No newline at end of file diff --git a/templates/.gitkeep b/templates/.gitkeep new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3