Change how config works, add missing models

This commit is contained in:
Zeva Rose 2023-03-24 11:36:21 -06:00
parent f0a4deaf72
commit 60090951cf
2 changed files with 26 additions and 144 deletions

View file

@ -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

View file

@ -1,6 +1,7 @@
"""JARVIS database models.""" """JARVIS database models."""
from datetime import datetime, timezone from datetime import datetime, timezone
from functools import partial from functools import partial
from typing import Optional
from beanie import Document, Link from beanie import Document, Link
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
@ -28,10 +29,12 @@ __all__ = [
"Purge", "Purge",
"Reminder", "Reminder",
"Rolegiver", "Rolegiver",
"Bypass",
"Roleping", "Roleping",
"Setting", "Setting",
"Subreddit", "Subreddit",
"SubredditFollow", "SubredditFollow",
"Tag",
"Temprole", "Temprole",
"TwitterAccount", "TwitterAccount",
"TwitterFollow", "TwitterFollow",
@ -74,6 +77,14 @@ class Config(Document):
value: str | int | bool value: str | int | bool
class Filter(Document):
"""Filter database object."""
guild: int
name: str
filters: list[str] = Field(default_factory=list)
class Guess(Document): class Guess(Document):
"""Guess database object.""" """Guess database object."""
@ -167,7 +178,7 @@ class Setting(Document):
guild: int guild: int
setting: str setting: str
value: str | int | bool value: str | int | bool | list[int | str]
class Pinboard(Document): class Pinboard(Document):
@ -189,10 +200,22 @@ class Pin(Document):
pinboard: Link[Pinboard] pinboard: Link[Pinboard]
guild: int guild: int
admin: int admin: int
star: int pin: int
created_at: datetime = NowField() 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): class Temprole(Document):
"""Temporary role object.""" """Temporary role object."""
@ -239,6 +262,7 @@ all_models = [
Setting, Setting,
Subreddit, Subreddit,
SubredditFollow, SubredditFollow,
Tag,
Temprole, Temprole,
TwitterAccount, TwitterAccount,
TwitterFollow, TwitterFollow,