import os from dataclasses import dataclass from dataclasses import field from pathlib import Path from typing import Optional from section7 import constants @dataclass class DatabaseSqliteConfig: """SQLite database backend configuration options :param path: Path to the SQLite database file """ path: Path = Path.cwd() / "section7.db" @classmethod def from_env(cls): """Build dataclass from environment""" return cls( path=Path(os.environ.get("SECTION7_DATABASE_SQLITE_PATH", cls.path)) .expanduser() .resolve(), ) @dataclass class DatabaseMariaConfig: """MariaDB database backend configuration options :param hostname: Hostname or IP address of the host running the database server :param username: Username of the account to use for connecting to the database server :param password: Password for the account to use for connecting to the database server :param port: Port on the host that the database server is listening on :param schema: Database schema that the application should use """ hostname: str = "localhost" username: str = "root" password: Optional[str] = None port: int = 3306 schema: str = "section7" @classmethod def from_env(cls): """Build dataclass from environment""" return cls( hostname=os.getenv("SECTION7_DATABASE_MARIADB_HOSTNAME", cls.hostname), username=os.getenv("SECTION7_DATABASE_MARIADB_USERNAME", cls.username), password=os.environ.get("SECTION7_DATABASE_MARIADB_PASSWORD", cls.password), port=int(os.environ.get("SECTION7_DATABASE_MARIADB_PORT", cls.port)), schema=os.getenv("SECTION7_DATABASE_MARIADB_SCHEMA", cls.schema), ) @dataclass class DatabaseConfig: """Database backend configuration :param backend: Enum selecting the backend to use for storing data :param sqlite: Container of SQLite settings :param mariadb: Container of MariaDB settings """ backend: constants.DatabaseBackend = constants.DatabaseBackend.SQLITE sqlite: DatabaseSqliteConfig = field(default_factory=DatabaseSqliteConfig.from_env) mariadb: DatabaseMariaConfig = field(default_factory=DatabaseMariaConfig.from_env) @classmethod def from_env(cls): """Build dataclass from environment""" return cls( backend=constants.DatabaseBackend[ os.environ["SECTION7_DATABASE_BACKEND"].upper() ] if "SECTION7_DATABASE_BACKEND" in os.environ else cls.backend, ) @dataclass class Section7Config: """Global application configuration settings :param database: Container of database backend settings :param contact_email: Public administrative contact email for the site """ database: DatabaseConfig = field(default_factory=DatabaseConfig.from_env) contact_email: str = "admin@enp.one" @classmethod def from_env(cls): """Build dataclass from environment""" return cls(contact_email=os.getenv("SECTION7_CONTACT_EMAIL", cls.contact_email)) def load() -> Section7Config: """Load the application configuration from environment variables :returns: Populated environment configuration """ try: return Section7Config.from_env() except (ValueError, TypeError, IndexError, KeyError) as err: raise RuntimeError("Failed to load application configuration") from err