diff --git a/projects/tentacles/BUILD b/projects/tentacles/BUILD index 336cd7d..4febfd8 100644 --- a/projects/tentacles/BUILD +++ b/projects/tentacles/BUILD @@ -6,12 +6,12 @@ py_project( main_deps = [ "//projects/anosql", "//projects/anosql-migrations", - "//projects/cherry-shim", py_requirement("attrs"), py_requirement("click"), py_requirement("flask"), py_requirement("jinja2"), py_requirement("octorest"), + py_requirement("cherrypy"), ], lib_data = [ "//projects/tentacles/src/python/tentacles/static/css", diff --git a/projects/tentacles/src/python/tentacles/__main__.py b/projects/tentacles/src/python/tentacles/__main__.py index 506aa0a..370915a 100644 --- a/projects/tentacles/src/python/tentacles/__main__.py +++ b/projects/tentacles/src/python/tentacles/__main__.py @@ -3,13 +3,13 @@ """The core app entrypoint.""" from datetime import datetime +import logging from pathlib import Path import tomllib +import cherrypy import click from flask import Flask, request - -from cherry_shim import shim from tentacles.blueprints import ( api, file_ui, @@ -19,7 +19,7 @@ from tentacles.blueprints import ( ) from tentacles.globals import _ctx, Ctx, ctx from tentacles.store import Store -from tentacles.workers import create_workers +from tentacles.workers import Worker @click.group() @@ -27,7 +27,7 @@ def cli(): pass -def store_factory(app): +def db_factory(app): store = Store(app.config.get("db", {}).get("uri")) store.connect() return store @@ -35,7 +35,7 @@ def store_factory(app): def custom_ctx(app, wsgi_app): def helper(environ, start_response): - store = store_factory(app) + store = db_factory(app) token = _ctx.set(Ctx(store)) try: return wsgi_app(environ, start_response) @@ -76,21 +76,11 @@ def user_session(): ctx.is_admin = user.group_id == 0 -@cli.command() -@click.option("--hostname", "hostname", type=str, default="0.0.0.0") -@click.option("--port", "port", type=int, default=8080) -@click.option("--config", type=Path) -def serve(hostname: str, port: int, config: Path): +def make_app(): app = Flask(__name__) - if config: - with open(config, "rb") as fp: - app.config.update(tomllib.load(fp)) - - print(app.config) # Before first request create_j2_request_global(app) - shutdown_event = create_workers(app, store_factory) # Before request app.before_request(user_session) @@ -104,13 +94,51 @@ def serve(hostname: str, port: int, config: Path): # Shove our middleware in there app.wsgi_app = custom_ctx(app, app.wsgi_app) - cherry = shim(app) - # And run the blame thing - try: - cherry.run(host=hostname, port=port) - finally: - shutdown_event.set() + return app + + +@cli.command() +@click.option("--hostname", "hostname", type=str, default="0.0.0.0") +@click.option("--port", "port", type=int, default=8080) +@click.option("--config", type=Path) +def serve(hostname: str, port: int, config: Path): + logging.basicConfig( + format="%(asctime)s %(relativeCreated)6d %(threadName)s - %(name)s - %(levelname)s - %(message)s", + level=logging.INFO, + ) + + logging.getLogger("tentacles").setLevel(logging.DEBUG) + + app = make_app() + + if config: + with open(config, "rb") as fp: + app.config.update(tomllib.load(fp)) + print(app.config) + + cherrypy.server.unsubscribe() + server = cherrypy._cpserver.Server() + cherrypy.config.update( + { + "environment": "production", + "engine.autoreload.on": False, + } + ) + cherrypy.tree.graft(app, "/") + + server.socket_host = hostname + server.socket_port = port + server.thread_pool = 16 + server.shutdown_timeout = 1 + server.subscribe() + + # Spawn the worker thread + Worker(cherrypy.engine, app, db_factory, frequency=5).start() + + # Run the server + cherrypy.engine.start() + cherrypy.engine.block() if __name__ == "__main__": diff --git a/projects/tentacles/src/python/tentacles/workers.py b/projects/tentacles/src/python/tentacles/workers.py index 410efca..6dafcdd 100644 --- a/projects/tentacles/src/python/tentacles/workers.py +++ b/projects/tentacles/src/python/tentacles/workers.py @@ -11,13 +11,13 @@ from contextlib import closing from datetime import datetime, timedelta import logging from pathlib import Path -from threading import Event, Thread +from threading import Event from time import sleep -from typing import Callable from urllib import parse as urlparse +from cherrypy.process.plugins import Monitor from fastmail import FastMailSMTP -from flask import Flask as App, render_template +from flask import Flask as App from octorest import OctoRest as _OR from requests import Response from requests.exceptions import ( @@ -271,18 +271,18 @@ def send_emails(app, store: Store): store.send_email(message.id) -@corn_job(timedelta(seconds=5)) -def run_worker(app: App, db_factory): - with app.app_context(), closing(db_factory(app)) as store: - poll_printers(app, store) - assign_jobs(app, store) - push_jobs(app, store) - revoke_jobs(app, store) - pull_jobs(app, store) - send_emails(app, store) +class Worker(Monitor): + def __init__(self, bus, app, db_factory, **kwargs): + self._app = app + self._db_factory = db_factory + super().__init__(bus, self.callback, **kwargs) - -def create_workers(app, db_factory: Callable[[App], Store]) -> Event: - Thread(target=run_worker, args=[app, db_factory]).start() - - return SHUTDOWN + def callback(self): + log.debug("Tick") + with self._app.app_context(), closing(self._db_factory(self._app)) as store: + poll_printers(self._app, store) + assign_jobs(self._app, store) + push_jobs(self._app, store) + revoke_jobs(self._app, store) + pull_jobs(self._app, store) + send_emails(self._app, store)