Skip to content

Commit

Permalink
Extract deserialization into _FromToml
Browse files Browse the repository at this point in the history
  • Loading branch information
purefunctor committed Jun 10, 2021
1 parent 85d547f commit 3618e43
Showing 1 changed file with 40 additions and 45 deletions.
85 changes: 40 additions & 45 deletions stlt/config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Reading and writing user configuration and secrets."""
from __future__ import annotations

from abc import ABC
from importlib import resources
from pathlib import Path
import typing as t
Expand All @@ -15,51 +16,61 @@
DEFAULT_CONFIG_FILE = toml.loads(resources.read_text(assets, "config.toml"))


_FromTomlType = t.TypeVar("_FromTomlType", bound="_FromToml")


class _FromToml(ABC):
"""Implements deserialization from `toml` into `attrs`."""

def __init__(self, *args: t.Any, **kwargs: t.Any) -> None: # pragma: no cover
...

@classmethod
def from_dict(cls: t.Type[_FromTomlType], data: t.Mapping) -> _FromTomlType:
"""Deserialize some `data` into an `attrs`-based `cls`."""
kwargs = {}

for field in attr.fields(cls):
name = field.name
meta = field.metadata

try:
if section := meta.get("section", False):
kwargs[name] = _nested_get(data, section)
else:
kwargs[name] = data[name]
except KeyError as e:
if "section" in meta:
err = f"Missing required section {e.args[0]}"
else:
err = f"Missing required key {name}"
raise ConfigError(err)

if builder := meta.get("builder", False):
kwargs[name] = builder(kwargs[name])

return cls(**kwargs)


@attr.s(slots=True)
class OAuthConfig:
class OAuthConfig(_FromToml):
"""Configuration data class for `SpotifyOAuth`."""

client_id: str = attr.ib()
client_secret: str = attr.ib()
redirect_uri: str = attr.ib()
scope: str = attr.ib()

@classmethod
def from_dict(cls, data: t.Mapping) -> OAuthConfig:
"""Create an `OAuthConfig` from a `toml`-based mapping."""
fields = {}
for field in attr.fields(cls):
name = field.name
try:
fields[name] = data[name]
except KeyError:
err = f"Missing required key '{name}'"
raise ConfigError(err) from None
return cls(**fields)


@attr.s
class CacheConfig:
class CacheConfig(_FromToml):
"""Configuration data class for the cache."""

auth_cache: Path = attr.ib(converter=Path)

@classmethod
def from_dict(cls, data: t.Mapping) -> CacheConfig:
"""Create a `CacheConfig` from a `toml`-based mapping."""
fields = {}
for field in attr.fields(cls):
name = field.name
try:
fields[name] = data[name]
except KeyError:
err = f"Missing required key '{name}'"
raise ConfigError(err) from None
return cls(**fields)


@attr.s(slots=True)
class Config:
class Config(_FromToml):
"""Configuration data class for the project."""

oauth: OAuthConfig = attr.ib(
Expand All @@ -70,22 +81,6 @@ class Config:
metadata={"section": ["cache"], "builder": CacheConfig.from_dict}
)

@classmethod
def from_dict(cls, data: t.Mapping) -> Config:
"""Create a `Config` from a `toml`-based mapping."""
sections = {}
for field in attr.fields(cls):
name = field.name
meta = field.metadata
builder = meta["builder"]
section = meta["section"]
try:
sections[name] = builder(_nested_get(data, section))
except KeyError as e:
err = f"Missing required section '{e.args[0]}'"
raise ConfigError(err) from None
return cls(**sections)


def ensure_config(config: Path) -> None:
"""Ensure that the `config` file exists and is valid."""
Expand Down

0 comments on commit 3618e43

Please sign in to comment.