aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app.py111
-rw-r--r--pysite/__init__.py3
-rw-r--r--pysite/base_route.py29
-rw-r--r--pysite/route_manager.py41
-rw-r--r--pysite/views/__init__.py3
-rw-r--r--pysite/views/error_handlers/__init__.py3
-rw-r--r--pysite/views/error_handlers/http_404.py14
-rw-r--r--pysite/views/healthcheck.py15
-rw-r--r--pysite/views/index.py12
-rw-r--r--pysite/views/invite.py15
-rw-r--r--requirements-ci.txt2
-rw-r--r--requirements.txt4
-rw-r--r--templates/.gitkeep0
13 files changed, 142 insertions, 110 deletions
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
--- /dev/null
+++ b/templates/.gitkeep