Change how config works, add missing models
This commit is contained in:
parent
f0a4deaf72
commit
60090951cf
2 changed files with 26 additions and 144 deletions
|
@ -1,142 +0,0 @@
|
|||
"""Load global config."""
|
||||
import ast
|
||||
from os import environ
|
||||
from lib2to3.pgen2 import token
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Type
|
||||
|
||||
import orjson as json
|
||||
import yaml
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
try:
|
||||
from yaml import CLoader as Loader
|
||||
except ImportError:
|
||||
from yaml import Loader
|
||||
|
||||
|
||||
DEFAULT_YAML = Path("config.yaml")
|
||||
DEFAULT_JSON = Path("config.json")
|
||||
|
||||
|
||||
class Config:
|
||||
REQUIRED: Dict[str, Type] = {}
|
||||
OPTIONAL: Dict[str, Dict[str, Any]] = {}
|
||||
|
||||
def __new__(cls, *args: list, **kwargs: dict):
|
||||
"""Create a new Config object."""
|
||||
|
||||
for arg, flags in cls.OPTIONAL.items():
|
||||
kwargs[arg] = kwargs.get(arg, flags["default"])
|
||||
|
||||
inst = super().__new__(cls, *args, **kwargs)
|
||||
inst._validate()
|
||||
|
||||
return inst
|
||||
|
||||
def _validate(self) -> None:
|
||||
for key in self.REQUIRED.keys():
|
||||
if getattr(self, key, None) is None:
|
||||
raise ValueError(f"Missing required key: {key}")
|
||||
|
||||
@classmethod
|
||||
def from_env(cls) -> "Config":
|
||||
"""Load the .env config file."""
|
||||
load_dotenv()
|
||||
data = {}
|
||||
for k, t in cls.REQUIRED.items():
|
||||
value = environ.get(k.upper(), None)
|
||||
if value and not isinstance(value, t):
|
||||
if t is dict:
|
||||
try:
|
||||
value = ast.literal_eval(value)
|
||||
except Exception:
|
||||
raise ValueError(f"{k} is a dict but is not formatted properly")
|
||||
elif t is bool:
|
||||
value = value.lower() in ["true", "yes", "t", "y", "1"]
|
||||
else:
|
||||
try:
|
||||
value = t(value)
|
||||
except Exception:
|
||||
continue
|
||||
data[k] = value
|
||||
for k, flags in cls.OPTIONAL.items():
|
||||
t = flags["type"]
|
||||
value = environ.get(k.upper(), flags["default"])
|
||||
if value and not isinstance(value, t):
|
||||
if t is dict:
|
||||
try:
|
||||
value = ast.literal_eval(value)
|
||||
except Exception:
|
||||
raise ValueError(f"{k} is a dict but is not formatted properly")
|
||||
elif t is bool:
|
||||
value = value.lower() in ["true", "yes", "t", "y", "1"]
|
||||
else:
|
||||
try:
|
||||
value = t(value)
|
||||
except Exception:
|
||||
continue
|
||||
data[k] = value
|
||||
|
||||
return cls(**data)
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, filepath: Path | str = DEFAULT_JSON) -> "Config":
|
||||
"""Load the json config file."""
|
||||
if inst := cls.__dict__.get("inst"):
|
||||
return inst
|
||||
|
||||
if isinstance(filepath, str):
|
||||
filepath = Path(filepath)
|
||||
|
||||
with filepath.open() as f:
|
||||
raw = f.read()
|
||||
|
||||
j = json.loads(raw)
|
||||
return cls(**j)
|
||||
|
||||
@classmethod
|
||||
def from_yaml(cls, filepath: Path | str = DEFAULT_YAML) -> "Config":
|
||||
"""Load the yaml config file."""
|
||||
if inst := cls.__dict__.get("inst"):
|
||||
return inst
|
||||
|
||||
if isinstance(filepath, str):
|
||||
filepath = Path(filepath)
|
||||
|
||||
with filepath.open() as f:
|
||||
raw = f.read()
|
||||
|
||||
y = yaml.load(raw, Loader=Loader)
|
||||
return cls(**y)
|
||||
|
||||
@classmethod
|
||||
def load(cls, method: Optional[str] = None) -> "Config":
|
||||
"""
|
||||
Load the config in a somewhat generic way.
|
||||
|
||||
Default load order is: yaml, json, env
|
||||
|
||||
Args:
|
||||
method: Load method, one of: yaml, json, env
|
||||
"""
|
||||
methods = ["yaml", "json", "env"]
|
||||
if method and method in methods:
|
||||
methods.remove(method)
|
||||
methods.insert(0, method)
|
||||
for method in methods:
|
||||
try:
|
||||
m = cls.__dict__.get(f"from_{method}", None)
|
||||
if m:
|
||||
return m()
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
raise ValueError("Unable to load configuration, please create one of: config.yaml, config.json, .env")
|
||||
|
||||
@classmethod
|
||||
def reload(cls) -> bool:
|
||||
"""Reload the config."""
|
||||
return cls.__dict__.pop("inst", None) is None
|
|
@ -1,6 +1,7 @@
|
|||
"""JARVIS database models."""
|
||||
from datetime import datetime, timezone
|
||||
from functools import partial
|
||||
from typing import Optional
|
||||
|
||||
from beanie import Document, Link
|
||||
from pydantic import BaseModel, Field
|
||||
|
@ -28,10 +29,12 @@ __all__ = [
|
|||
"Purge",
|
||||
"Reminder",
|
||||
"Rolegiver",
|
||||
"Bypass",
|
||||
"Roleping",
|
||||
"Setting",
|
||||
"Subreddit",
|
||||
"SubredditFollow",
|
||||
"Tag",
|
||||
"Temprole",
|
||||
"TwitterAccount",
|
||||
"TwitterFollow",
|
||||
|
@ -74,6 +77,14 @@ class Config(Document):
|
|||
value: str | int | bool
|
||||
|
||||
|
||||
class Filter(Document):
|
||||
"""Filter database object."""
|
||||
|
||||
guild: int
|
||||
name: str
|
||||
filters: list[str] = Field(default_factory=list)
|
||||
|
||||
|
||||
class Guess(Document):
|
||||
"""Guess database object."""
|
||||
|
||||
|
@ -167,7 +178,7 @@ class Setting(Document):
|
|||
|
||||
guild: int
|
||||
setting: str
|
||||
value: str | int | bool
|
||||
value: str | int | bool | list[int | str]
|
||||
|
||||
|
||||
class Pinboard(Document):
|
||||
|
@ -189,10 +200,22 @@ class Pin(Document):
|
|||
pinboard: Link[Pinboard]
|
||||
guild: int
|
||||
admin: int
|
||||
star: int
|
||||
pin: int
|
||||
created_at: datetime = NowField()
|
||||
|
||||
|
||||
class Tag(Document):
|
||||
"""Tag database object."""
|
||||
|
||||
creator: int
|
||||
name: str
|
||||
content: str
|
||||
guild: int
|
||||
created_at: datetime = NowField()
|
||||
edited_at: Optional[datetime] = None
|
||||
editor: Optional[int] = None
|
||||
|
||||
|
||||
class Temprole(Document):
|
||||
"""Temporary role object."""
|
||||
|
||||
|
@ -239,6 +262,7 @@ all_models = [
|
|||
Setting,
|
||||
Subreddit,
|
||||
SubredditFollow,
|
||||
Tag,
|
||||
Temprole,
|
||||
TwitterAccount,
|
||||
TwitterFollow,
|
||||
|
|
Loading…
Add table
Reference in a new issue