aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--.snekrc3
-rw-r--r--app.py35
-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.txt1
13 files changed, 145 insertions, 35 deletions
diff --git a/.gitignore b/.gitignore
index 7bbc71c0..6669a983 100644
--- a/.gitignore
+++ b/.gitignore
@@ -94,8 +94,8 @@ ENV/
# Rope project settings
.ropeproject
-# mkdocs documentation
-/site
-
# mypy
.mypy_cache/
+
+# PyCharm
+.idea/
diff --git a/.snekrc b/.snekrc
index d8b7be64..3f26eea5 100644
--- a/.snekrc
+++ b/.snekrc
@@ -2,4 +2,5 @@
linters = flake8, safety, dodgy
[flake8]
-max-line-length=100 \ No newline at end of file
+max-line-length=120
+application_import_names=pysite \ No newline at end of file
diff --git a/app.py b/app.py
index 7d1f3dcf..f3bb9a60 100644
--- a/app.py
+++ b/app.py
@@ -1,35 +1,10 @@
-#!/usr/bin/env python3
+# coding=utf-8
-import os
+from pysite.route_manager import RouteManager
-from flask import Flask
-from flask import jsonify
-from flask import redirect
-
-app = Flask(__name__)
-
-app.secret_key = os.environ.get("WEBPAGE_SECRET_KEY")
-
-
-def index():
- return "Coming soon:tm:"
-
-
-def invite():
- return redirect("http://invite.pythondiscord.com/")
-
-
[email protected]("/healthcheck")
-def healthcheck():
- return jsonify({"status": "ok"})
-
-
-def page_not_found(e):
- return "replace me with a template, 404 not found", 404
+manager = RouteManager()
+app = manager.app
if __name__ == '__main__':
- app.run(port=int(os.environ.get("WEBPAGE_PORT")), debug=False)
+ 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..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-ci.txt b/requirements-ci.txt
index 85205d10..1ae17a79 100644
--- a/requirements-ci.txt
+++ b/requirements-ci.txt
@@ -8,4 +8,3 @@ flake8-todo
flake8-string-format
safety
dodgy
-