diff --git a/activitypub/manager/base.py b/activitypub/manager/base.py index e5bf2c0..bb2e294 100644 --- a/activitypub/manager/base.py +++ b/activitypub/manager/base.py @@ -2,6 +2,52 @@ import binascii import os import uuid +class Data(): + routes = [] + filters = [] + +class DataWrapper(): + """ + Instance for saving routes, filters, etc. for app. + + >>> @app.filter + ... def upper(item): + ... return item.upper() + >>> len(app._data.filters) == 1 + True + >>> app._data.filters[0]("testing") + 'TESTING' + >>> class Test(): + ... @app.route("/test") + ... def route_test(self, *args, **kwargs): + ... print(args, kwargs) + ... return 42 + >>> t = Test() + >>> len(app._data.routes) == 1 + True + >>> path, methods, function = app._data.routes[0] + >>> function(t, 1, 2, 3, hello="world") + (1, 2, 3) {'hello': 'world'} + 42 + """ + _data = Data() + + def filter(self, f): + """ + Wrap a plain function/method to provide template function. + """ + self._data.filters.append(f) + return f + + def route(self, path, methods=None): + """ + Wrap a function/method as a route. + """ + def decorator(f): + self._data.routes.append((path, methods, f)) + return f + return decorator + class Routes(): routes = [] @@ -45,6 +91,19 @@ class Manager(): for class_ in ActivityPubBase.CLASSES: setattr(self, class_, make_wrapper(self, class_)) + def setup_css(self, folder="."): + import sass + THEME_STYLE = "light" + THEME_COLOR = "#1d781d" + SASS_DIR = os.path.join(os.path.abspath(folder), "sass") + theme_css = f"$primary-color: {THEME_COLOR};\n" + with open(os.path.join(SASS_DIR, f"{THEME_STYLE}.scss")) as f: + theme_css += f.read() + theme_css += "\n" + with open(os.path.join(SASS_DIR, "base_theme.scss")) as f: + raw_css = theme_css + f.read() + self.CSS = sass.compile(string=raw_css, output_style="compressed") + def make_defaults(self): """ A default field can refer to itself, which means that it needs a @@ -249,3 +308,8 @@ class Manager(): def decorator(function): return function return decorator + +## Singleton for the Application +## Allows it to be in scope for decorating the app's +## methods and functions +app = DataWrapper() diff --git a/activitypub/manager/flaskman.py b/activitypub/manager/flaskman.py index bc9e69e..92e7c29 100644 --- a/activitypub/manager/flaskman.py +++ b/activitypub/manager/flaskman.py @@ -1,36 +1,58 @@ -from flask import (Flask, Response, abort, - jsonify as flask_jsonify, - redirect, render_template, - request, session, url_for) -from flask_wtf.csrf import CSRFProtect +try: + from flask import (Flask, Response, abort, + jsonify as flask_jsonify, + redirect, render_template, + request, session, url_for) + from flask_wtf.csrf import CSRFProtect +except: + pass # flask not available -from .base import Manager +import inspect -class FlaskRoutes(): - def __init__(self, manager): - self.manager = manager +from .base import Manager, app - def __call__(self, path, methods=["GET"]): - print("Calling FlaskRoutes() with path=", path) - def decorator(function): - print("wrapping!") - @self.manager.app.route(path) - def f(*args, **kwargs): - print("calling wrapped function!") - function(*args, **kwargs) - print("returning") - return decorator +def wrap_method(self, f): + def function(*args, **kwargs): + print(f.__name__, "called with:", args, kwargs) + return f(self, *args, **kwargs) + function.__name__ = f.__name__ + return function class FlaskManager(Manager): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.app = Flask(__name__) - self.app.config.update(WTF_CSRF_CHECK_DEFAULT=False) - self.csrf = CSRFProtect(self.app) - print("here!") - self.route = FlaskRoutes(self) def run(self): + self.app = Flask(__name__, + template_folder="/home/dblank/activitypub/apps/blog/templates/", + static_folder="/home/dblank/activitypub/apps/blog/static") + self.app.config.update(WTF_CSRF_CHECK_DEFAULT=False) + self.csrf = CSRFProtect(self.app) + + #self.app.config["EXPLAIN_TEMPLATE_LOADING"] = True + self.app.config["ME"] = {"url": "https://example.com", + "icon": {"url": "https://example.com"}, + "icon_url": 'https://cs.brynmawr.edu/~dblank/images/doug-sm-orig.jpg', + "summary": "I'm just me."} + self.app.config["CSS"] = self.CSS + self.app.config["NAME"] = "ActivityPub Blog" + self.app.config["ID"] = "http://localhost:5000" + ## Add routes: + for path, methods, f in app._data.routes: + params = [x.name for x in inspect.signature(f).parameters.values()] + print(f.__name__, params) + if len(params) > 0 and params[0] == "self": + self.app.route(path)(wrap_method(self, f)) + else: + self.app.route(path)(f) + ## Add filters: + for f in app._data.filters: + params = [x.name for x in inspect.signature(f).parameters.values()] + print(f.__name__, params) + if len(params) > 0 and params[0] == "self": + self.app.template_filter()(wrap_method(self, f)) + else: + self.app.template_filter()(f) self.app.run(debug=1) def load_secret_key(self, name): diff --git a/activitypub/manager/tornadoman.py b/activitypub/manager/tornadoman.py index 49d9f9c..8338cec 100644 --- a/activitypub/manager/tornadoman.py +++ b/activitypub/manager/tornadoman.py @@ -1,5 +1,8 @@ -import tornado -from tornado.web import (Application, RequestHandler) +try: + import tornado + from tornado.web import (Application, RequestHandler) +except: + pass # tornado not available from .base import Manager diff --git a/tests/test_all.py b/tests/test_all.py index 22b97af..4850401 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -28,7 +28,7 @@ def test_all(): p1 = manager.Person(id="alyssa") p2 = manager.Person(id="brenda") - + manager.database.actors.insert_one(p1.to_dict()) manager.database.actors.insert_one(p2.to_dict())