diff options
author | 2018-02-05 20:26:54 +0100 | |
---|---|---|
committer | 2018-02-05 20:26:54 +0100 | |
commit | 80a1ab522e7a49f3ea1168d8658fbff293c866f1 (patch) | |
tree | 206a45b1a68c273db43ad04b105bcd3e7c48f565 | |
parent | Dynamic route loader; proper application structure (diff) |
Major update
- Switch to Japronto
- More linters
- Rewrite route handling
- Rewrite error handling
- Rewrite static handling
- Error when no `Index` propery is found
- Probably some more stuff? idk
Code needs testing; Maybe we could use pytest?
Signed-off-by: martmists <[email protected]>
-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, 155 insertions, 144 deletions
@@ -1,6 +1,21 @@ [all] -linters = flake8, safety, dodgy +linters = flake8, safety, dodgy, yapf, isort [flake8] max-line-length=120 -application_import_names=pysite
\ No newline at end of file +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 @@ -1,10 +1,108 @@ -# coding=utf-8 +#!/usr/bin/env python3.6 -from pysite.route_manager import RouteManager +# Stdlib +from importlib.util import module_from_spec, spec_from_file_location +import mimetypes +import os +# External Libraries +from japronto import Application -manager = RouteManager() -app = manager.app +app = Application() -if __name__ == '__main__': - manager.run() + +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 route in routes: + app.router.add_route(route.path, route.run, method=route.method) + +for path, method, handle in static: + app.router.add_route(path, handle, method=method) + +for errcode, handler in errors: + app.add_error_handler(errcode, handler) + +app.run(debug=True, port=80) diff --git a/error_handlers/http_404.py b/error_handlers/http_404.py new file mode 100644 index 00000000..712bf748 --- /dev/null +++ b/error_handlers/http_404.py @@ -0,0 +1,8 @@ +# 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 deleted file mode 100644 index ba286add..00000000 --- a/pysite/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# coding=utf-8 - -__author__ = "Gareth Coles" diff --git a/pysite/base_route.py b/pysite/base_route.py deleted file mode 100644 index 76338280..00000000 --- a/pysite/base_route.py +++ /dev/null @@ -1,29 +0,0 @@ -# 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 deleted file mode 100644 index 501076b7..00000000 --- a/pysite/route_manager.py +++ /dev/null @@ -1,41 +0,0 @@ -# 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 deleted file mode 100644 index ba286add..00000000 --- a/pysite/views/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# coding=utf-8 - -__author__ = "Gareth Coles" diff --git a/pysite/views/error_handlers/__init__.py b/pysite/views/error_handlers/__init__.py deleted file mode 100644 index ba286add..00000000 --- a/pysite/views/error_handlers/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# coding=utf-8 - -__author__ = "Gareth Coles" diff --git a/pysite/views/error_handlers/http_404.py b/pysite/views/error_handlers/http_404.py deleted file mode 100644 index eea1e630..00000000 --- a/pysite/views/error_handlers/http_404.py +++ /dev/null @@ -1,14 +0,0 @@ -# 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 deleted file mode 100644 index 660c8a96..00000000 --- a/pysite/views/healthcheck.py +++ /dev/null @@ -1,15 +0,0 @@ -# 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 deleted file mode 100644 index 2e779003..00000000 --- a/pysite/views/index.py +++ /dev/null @@ -1,12 +0,0 @@ -# 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 deleted file mode 100644 index d035fc99..00000000 --- a/pysite/views/invite.py +++ /dev/null @@ -1,15 +0,0 @@ -# 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 69ca547f..8f1f53e4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -flask==0.12.2
\ No newline at end of file +japronto diff --git a/routes/healthcheck.py b/routes/healthcheck.py new file mode 100644 index 00000000..11a5fc24 --- /dev/null +++ b/routes/healthcheck.py @@ -0,0 +1,8 @@ +# 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 new file mode 100644 index 00000000..76671ed1 --- /dev/null +++ b/routes/index.py @@ -0,0 +1,8 @@ +# 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 new file mode 100644 index 00000000..29553345 --- /dev/null +++ b/routes/invite.py @@ -0,0 +1,9 @@ +# 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 deleted file mode 100644 index e69de29b..00000000 --- a/templates/.gitkeep +++ /dev/null |