Remove config, YAGNI
This commit is contained in:
parent
63d9081e07
commit
5e262deda6
5 changed files with 15 additions and 165 deletions
|
@ -1,152 +0,0 @@
|
||||||
"""Very simple config loading library"""
|
|
||||||
from schema import Schema, Use, Optional
|
|
||||||
import matchy.files.ops as ops
|
|
||||||
import os
|
|
||||||
import logging
|
|
||||||
import json
|
|
||||||
|
|
||||||
logger = logging.getLogger("config")
|
|
||||||
logger.setLevel(logging.INFO)
|
|
||||||
|
|
||||||
# Envar takes precedent
|
|
||||||
_ENVAR = "MATCHY_CONFIG"
|
|
||||||
_FILE = ".matchy/config.json"
|
|
||||||
|
|
||||||
# Warning: Changing any of the below needs proper thought to ensure backwards compatibility
|
|
||||||
_VERSION = 2
|
|
||||||
|
|
||||||
|
|
||||||
class _Key():
|
|
||||||
VERSION = "version"
|
|
||||||
|
|
||||||
MATCH = "match"
|
|
||||||
|
|
||||||
SCORE_FACTORS = "score_factors"
|
|
||||||
REPEAT_ROLE = "repeat_role"
|
|
||||||
REPEAT_MATCH = "repeat_match"
|
|
||||||
EXTRA_MEMBER = "extra_member"
|
|
||||||
UPPER_THRESHOLD = "upper_threshold"
|
|
||||||
|
|
||||||
# Removed
|
|
||||||
_OWNERS = "owners"
|
|
||||||
_TOKEN = "token"
|
|
||||||
|
|
||||||
|
|
||||||
_SCHEMA = Schema(
|
|
||||||
{
|
|
||||||
# The current version
|
|
||||||
_Key.VERSION: Use(int),
|
|
||||||
|
|
||||||
# Settings for the match algorithmn, see matching.py for explanations on usage
|
|
||||||
Optional(_Key.MATCH): {
|
|
||||||
Optional(_Key.SCORE_FACTORS): {
|
|
||||||
|
|
||||||
Optional(_Key.REPEAT_ROLE): Use(int),
|
|
||||||
Optional(_Key.REPEAT_MATCH): Use(int),
|
|
||||||
Optional(_Key.EXTRA_MEMBER): Use(int),
|
|
||||||
Optional(_Key.UPPER_THRESHOLD): Use(int),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
_EMPTY_DICT = {
|
|
||||||
_Key.VERSION: _VERSION
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _migrate_to_v1(d: dict):
|
|
||||||
# Owners moved to History in v1
|
|
||||||
# Note: owners will be required to be re-added to the state.json
|
|
||||||
if _Key._OWNERS in d:
|
|
||||||
owners = d.pop(_Key._OWNERS)
|
|
||||||
logger.warning(
|
|
||||||
"Migration removed owners from config, these must be re-added to the state.json")
|
|
||||||
logger.warning("Owners: %s", owners)
|
|
||||||
|
|
||||||
|
|
||||||
def _migrate_to_v2(d: dict):
|
|
||||||
# Token moved to the environment
|
|
||||||
if _Key._TOKEN in d:
|
|
||||||
del d[_Key._TOKEN]
|
|
||||||
|
|
||||||
|
|
||||||
# Set of migration functions to apply
|
|
||||||
_MIGRATIONS = [
|
|
||||||
_migrate_to_v1,
|
|
||||||
_migrate_to_v2
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _ScoreFactors():
|
|
||||||
def __init__(self, data: dict):
|
|
||||||
"""Initialise and validate the config"""
|
|
||||||
self._dict = data
|
|
||||||
|
|
||||||
@property
|
|
||||||
def repeat_role(self) -> int:
|
|
||||||
return self._dict.get(_Key.REPEAT_ROLE, None)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def repeat_match(self) -> int:
|
|
||||||
return self._dict.get(_Key.REPEAT_MATCH, None)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def extra_member(self) -> int:
|
|
||||||
return self._dict.get(_Key.EXTRA_MEMBER, None)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def upper_threshold(self) -> int:
|
|
||||||
return self._dict.get(_Key.UPPER_THRESHOLD, None)
|
|
||||||
|
|
||||||
|
|
||||||
class _Config():
|
|
||||||
def __init__(self, data: dict):
|
|
||||||
"""Initialise and validate the config"""
|
|
||||||
_SCHEMA.validate(data)
|
|
||||||
self._dict = data
|
|
||||||
|
|
||||||
@property
|
|
||||||
def token(self) -> str:
|
|
||||||
return self._dict["token"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def score_factors(self) -> _ScoreFactors:
|
|
||||||
return _ScoreFactors(self._dict.get(_Key.SCORE_FACTORS, {}))
|
|
||||||
|
|
||||||
|
|
||||||
def _migrate(dict: dict):
|
|
||||||
"""Migrate a dict through versions"""
|
|
||||||
version = dict.get("version", 0)
|
|
||||||
for i in range(version, _VERSION):
|
|
||||||
_MIGRATIONS[i](dict)
|
|
||||||
dict["version"] = _VERSION
|
|
||||||
|
|
||||||
|
|
||||||
def _load() -> _Config:
|
|
||||||
"""
|
|
||||||
Load the state from an envar or file
|
|
||||||
Apply any required migrations
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Try the envar first
|
|
||||||
envar = os.environ.get(_ENVAR)
|
|
||||||
if envar:
|
|
||||||
loaded = json.loads(envar)
|
|
||||||
logger.info("Config loaded from $%s", _ENVAR)
|
|
||||||
else:
|
|
||||||
# Otherwise try the file
|
|
||||||
if os.path.isfile(_FILE):
|
|
||||||
loaded = ops.load(_FILE)
|
|
||||||
logger.info("Config loaded from %s", _FILE)
|
|
||||||
else:
|
|
||||||
loaded = _EMPTY_DICT
|
|
||||||
logger.warning("No %s file found, using defaults", _FILE)
|
|
||||||
|
|
||||||
_migrate(loaded)
|
|
||||||
return _Config(loaded)
|
|
||||||
|
|
||||||
|
|
||||||
# Core config for users to use
|
|
||||||
# Singleton as there should only be one, it's static, and global
|
|
||||||
Config = _load()
|
|
|
@ -191,7 +191,7 @@ class State():
|
||||||
before storing the dict back in the State
|
before storing the dict back in the State
|
||||||
"""
|
"""
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def inner(self: State, *args, **kwargs):
|
def inner(self, *args, **kwargs):
|
||||||
tmp = State(self._dict, self._file)
|
tmp = State(self._dict, self._file)
|
||||||
func(tmp, *args, **kwargs)
|
func(tmp, *args, **kwargs)
|
||||||
_SCHEMA.validate(tmp._dict)
|
_SCHEMA.validate(tmp._dict)
|
||||||
|
|
|
@ -5,7 +5,6 @@ from datetime import datetime
|
||||||
from typing import Protocol, runtime_checkable
|
from typing import Protocol, runtime_checkable
|
||||||
from matchy.files.state import State, ts_to_datetime
|
from matchy.files.state import State, ts_to_datetime
|
||||||
import matchy.util as util
|
import matchy.util as util
|
||||||
import matchy.files.config as config
|
|
||||||
|
|
||||||
|
|
||||||
class _ScoreFactors(int):
|
class _ScoreFactors(int):
|
||||||
|
@ -15,14 +14,14 @@ class _ScoreFactors(int):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Added for each role the matchee has that another group member has
|
# Added for each role the matchee has that another group member has
|
||||||
REPEAT_ROLE = config.Config.score_factors.repeat_role or 2**2
|
REPEAT_ROLE = 2**2
|
||||||
# Added for each member in the group that the matchee has already matched with
|
# Added for each member in the group that the matchee has already matched with
|
||||||
REPEAT_MATCH = config.Config.score_factors.repeat_match or 2**3
|
REPEAT_MATCH = 2**3
|
||||||
# Added for each additional member over the set "per group" value
|
# Added for each additional member over the set "per group" value
|
||||||
EXTRA_MEMBER = config.Config.score_factors.extra_member or 2**5
|
EXTRA_MEMBER = 2**5
|
||||||
|
|
||||||
# Upper threshold, if the user scores higher than this they will not be placed in that group
|
# Upper threshold, if the user scores higher than this they will not be placed in that group
|
||||||
UPPER_THRESHOLD = config.Config.score_factors.upper_threshold or 2**6
|
UPPER_THRESHOLD = 2**6
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger("matching")
|
logger = logging.getLogger("matching")
|
||||||
|
|
|
@ -403,7 +403,8 @@ def test_auth_scopes():
|
||||||
tmp_state.set_user_scope(id, state.AuthScope.MATCHER)
|
tmp_state.set_user_scope(id, state.AuthScope.MATCHER)
|
||||||
assert tmp_state.get_user_has_scope(id, state.AuthScope.MATCHER)
|
assert tmp_state.get_user_has_scope(id, state.AuthScope.MATCHER)
|
||||||
|
|
||||||
tmp_state.validate()
|
# Validate the state by constucting a new one
|
||||||
|
_ = state.State(tmp_state._dict)
|
||||||
|
|
||||||
|
|
||||||
def test_iterate_all_shifts():
|
def test_iterate_all_shifts():
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import matchy.files.state as state
|
import matchy.files.state as state
|
||||||
import tempfile
|
import tempfile
|
||||||
import os
|
import os
|
||||||
|
import matchy.files.ops as ops
|
||||||
|
|
||||||
|
|
||||||
def test_basic_state():
|
def test_basic_state():
|
||||||
|
@ -18,10 +19,11 @@ def test_simple_load_reload():
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
path = os.path.join(tmp, 'tmp.json')
|
path = os.path.join(tmp, 'tmp.json')
|
||||||
st = state.load_from_file(path)
|
st = state.load_from_file(path)
|
||||||
st._save_to_file()
|
ops.save(st._file, st._dict)
|
||||||
|
ops.save(st._file, st._dict)
|
||||||
|
|
||||||
st = state.load_from_file(path)
|
st = state.load_from_file(path)
|
||||||
st._save_to_file()
|
ops.save(st._file, st._dict)
|
||||||
st = state.load_from_file(path)
|
st = state.load_from_file(path)
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,13 +32,13 @@ def test_authscope():
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
path = os.path.join(tmp, 'tmp.json')
|
path = os.path.join(tmp, 'tmp.json')
|
||||||
st = state.load_from_file(path)
|
st = state.load_from_file(path)
|
||||||
st._save_to_file()
|
ops.save(st._file, st._dict)
|
||||||
|
|
||||||
assert not st.get_user_has_scope(1, state.AuthScope.MATCHER)
|
assert not st.get_user_has_scope(1, state.AuthScope.MATCHER)
|
||||||
|
|
||||||
st = state.load_from_file(path)
|
st = state.load_from_file(path)
|
||||||
st.set_user_scope(1, state.AuthScope.MATCHER)
|
st.set_user_scope(1, state.AuthScope.MATCHER)
|
||||||
st._save_to_file()
|
ops.save(st._file, st._dict)
|
||||||
|
|
||||||
st = state.load_from_file(path)
|
st = state.load_from_file(path)
|
||||||
assert st.get_user_has_scope(1, state.AuthScope.MATCHER)
|
assert st.get_user_has_scope(1, state.AuthScope.MATCHER)
|
||||||
|
@ -50,13 +52,13 @@ def test_channeljoin():
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
path = os.path.join(tmp, 'tmp.json')
|
path = os.path.join(tmp, 'tmp.json')
|
||||||
st = state.load_from_file(path)
|
st = state.load_from_file(path)
|
||||||
st._save_to_file()
|
ops.save(st._file, st._dict)
|
||||||
|
|
||||||
assert not st.get_user_active_in_channel(1, "2")
|
assert not st.get_user_active_in_channel(1, "2")
|
||||||
|
|
||||||
st = state.load_from_file(path)
|
st = state.load_from_file(path)
|
||||||
st.set_user_active_in_channel(1, "2", True)
|
st.set_user_active_in_channel(1, "2", True)
|
||||||
st._save_to_file()
|
ops.save(st._file, st._dict)
|
||||||
|
|
||||||
st = state.load_from_file(path)
|
st = state.load_from_file(path)
|
||||||
assert st.get_user_active_in_channel(1, "2")
|
assert st.get_user_active_in_channel(1, "2")
|
||||||
|
|
Loading…
Add table
Reference in a new issue