diff --git a/amanuensis/config/__init__.py b/amanuensis/config/__init__.py index e202d47..fa62079 100644 --- a/amanuensis/config/__init__.py +++ b/amanuensis/config/__init__.py @@ -1,23 +1,23 @@ -# Module imports -from .dict import AttrOrderedDict, ReadOnlyOrderedDict -from .directory import ( - RootConfigDirectoryContext, - UserConfigDirectoryContext, - LexiconConfigDirectoryContext, - is_guid) +import argparse +from typing import Optional +import os -# Environment variable name constants -ENV_SECRET_KEY = "AMANUENSIS_SECRET_KEY" -ENV_CONFIG_DIR = "AMANUENSIS_CONFIG_DIR" -ENV_LOG_FILE = "AMANUENSIS_LOG_FILE" -ENV_LOG_FILE_SIZE = "AMANUENSIS_LOG_FILE_SIZE" -ENV_LOG_FILE_NUM = "AMANUENSIS_LOG_FILE_NUM" -__all__ = [ - AttrOrderedDict.__name__, - ReadOnlyOrderedDict.__name__, - RootConfigDirectoryContext.__name__, - UserConfigDirectoryContext.__name__, - LexiconConfigDirectoryContext.__name__, - is_guid.__name__, -] +class AmanuensisConfig: + """Base config type. Defines config keys for subclasses to override.""" + # If AMANUENSIS_CONFIG is defined, the config file it points to may override + # config values defined on the config object itself. + AMANUENSIS_CONFIG: Optional[str] = None + STATIC_ROOT: Optional[str] = "static" + SECRET_KEY: Optional[str] = None + DATABASE_URI: Optional[str] = None + TESTING = False + + +class EnvironmentConfig(AmanuensisConfig): + """Loads config values from environment variables.""" + AMANUENSIS_CONFIG = os.environ.get("AMANUENSIS_CONFIG", AmanuensisConfig.AMANUENSIS_CONFIG) + STATIC_ROOT = os.environ.get("AMANUENSIS_STATIC_ROOT", AmanuensisConfig.STATIC_ROOT) + + +# def get_cli_config(): diff --git a/amanuensis/db/database.py b/amanuensis/db/database.py index 254a488..0217613 100644 --- a/amanuensis/db/database.py +++ b/amanuensis/db/database.py @@ -3,8 +3,12 @@ Database connection setup """ from sqlalchemy import create_engine, MetaData, event from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import scoped_session -from sqlalchemy.orm import sessionmaker +from sqlalchemy.orm import scoped_session, sessionmaker + +try: + from greenlet import getcurrent as get_ident +except ImportError: + from threading import get_ident # Define naming conventions for generated constraints @@ -34,7 +38,7 @@ class DbContext: cursor.close() # Create a thread-safe session factory - self.session = scoped_session(sessionmaker(bind=self.engine)) + self.session = scoped_session(sessionmaker(bind=self.engine), scopefunc=get_ident) def __call__(self, *args, **kwargs): """Provides shortcut access to session.execute.""" diff --git a/amanuensis/db/models.py b/amanuensis/db/models.py index 471e05a..984deed 100644 --- a/amanuensis/db/models.py +++ b/amanuensis/db/models.py @@ -435,6 +435,9 @@ class IndexType(enum.Enum): PREFIX = 2 ETC = 3 + def __str__(self) -> str: + return str(self.name).lower() + class ArticleIndex(ModelBase): """ diff --git a/amanuensis/server/__init__.py b/amanuensis/server/__init__.py index 41fe451..3938138 100644 --- a/amanuensis/server/__init__.py +++ b/amanuensis/server/__init__.py @@ -1,46 +1,67 @@ -import os +from datetime import datetime +import json -from flask import Flask +from flask import Flask, g -from amanuensis.config import RootConfigDirectoryContext, ENV_CONFIG_DIR -from amanuensis.models import ModelFactory -from .auth import get_login_manager, bp_auth -from .helpers import register_custom_filters -from .home import bp_home -from .lexicon import bp_lexicon -from .session import bp_session +from amanuensis.config import AmanuensisConfig, EnvironmentConfig +from amanuensis.db import DbContext +# from amanuensis.models import ModelFactory +# from .auth import get_login_manager, bp_auth +# from .helpers import register_custom_filters +# from .home import bp_home +# from .lexicon import bp_lexicon +# from .session import bp_session -def get_app(root: RootConfigDirectoryContext) -> Flask: - # Flask app init - with root.read_config() as cfg: - app = Flask( - __name__, - template_folder='.', - static_folder=cfg.static_root - ) - app.secret_key = bytes.fromhex(cfg.secret_key) - app.config['root'] = root - app.config['model_factory'] = ModelFactory(root) - app.jinja_options['trim_blocks'] = True - app.jinja_options['lstrip_blocks'] = True - register_custom_filters(app) +def get_app(config: AmanuensisConfig) -> Flask: + """Application factory""" + # Create the Flask object + app = Flask(__name__, template_folder=".", static_folder=config.STATIC_ROOT) - # Flask-Login init - login_manager = get_login_manager(root) - login_manager.init_app(app) + # Load keys from the config object + app.config.from_object(config) - # Blueprint inits - app.register_blueprint(bp_auth) - app.register_blueprint(bp_home) - app.register_blueprint(bp_lexicon) - app.register_blueprint(bp_session) + # If a config file is now specified, also load keys from there + if app.config.get("AMANUENSIS_CONFIG", None): + app.config.from_file(app.config["AMANUENSIS_CONFIG"], json.load) - return app + # Assert that all required config values are now set + for config_key in ("SECRET_KEY", "DATABASE_URI"): + if not app.config.get(config_key): + raise Exception(f"{config_key} must be defined") + + # Create the database context + db = DbContext(app.config["DATABASE_URI"]) + + # Make the database connection available to requests via g + def db_setup(): + g.db = db + app.before_request(db_setup) + + # Tear down the session on request teardown + def db_teardown(response_or_exc): + db.session.remove() + app.teardown_appcontext(db_teardown) + + # Configure jinja options + app.jinja_options.update(trim_blocks=True, lstrip_blocks=True) + + # Flask-Login init + # login_manager = get_login_manager(root) + # login_manager.init_app(app) + + # Blueprint inits + # app.register_blueprint(bp_auth) + # app.register_blueprint(bp_home) + # app.register_blueprint(bp_lexicon) + # app.register_blueprint(bp_session) + + return app -def default(): - cwd = os.getcwd() - config_dir = os.environ.get(ENV_CONFIG_DIR, "amanuensis") - root = RootConfigDirectoryContext(os.path.join(cwd, config_dir)) - return get_app(root) +# def wsgi() -> Flask: +# return get_app(EnvironmentConfig()) + + +# def run(): +# wsgi().run() diff --git a/pyproject.toml b/pyproject.toml index 48e95d6..fd890c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,9 @@ pytest = "^5.2" black = "^21.5b2" mypy = "^0.812" +[tool.poetry.scripts] +amanuensis-server = "amanuensis.server:run" + [tool.black] extend-exclude = "^/amanuensis/cli/.*|^/amanuensis/config/.*|^/amanuensis/lexicon/.*|^/amanuensis/log/.*|^/amanuensis/models/.*|^/amanuensis/resources/.*|^/amanuensis/server/.*|^/amanuensis/user/.*|^/amanuensis/__main__.py"