Recipes¶
Sourcerer
Recipe for Accepting Different Data Types¶
This example is simplified adaptation from the
bobbie
configuration settings
repository. This code sets up a Settings class that will call a different
construction method depending on the type of data passed:
import configparser
import dataclasses
import pathlib
from collections.abc import Callable, MutableMapping
from typing import Any, ClassVar
import yaml
import wonka
_FILE_EXTENSIONS: dict[str, str] = {
'ini': 'ini',
'yaml': 'yaml',
'yml': 'yaml'}
_LOAD_METHOD: Callable[[str], str] = lambda x: f'from_{x}'
@dataclasses.dataclass
class Settings(wonka.Sourcerer):
contents: MutableMapping[str, Any] = dataclasses.field(
default_factory = dict)
sources: ClassVar[MutableMapping[type[Any], str]] = {
pathlib.Path: 'file',
str: 'file',
MutableMapping: 'dict'}
@classmethod
def from_dict(cls, source: MutableMapping[str, Any]) -> Settings:
return cls(source)
@classmethod
def from_file(cls, source: str | pathlib.Path) -> Settings:
if isinstance(source, str):
source = pathlib.Path(item)
if source.is_file():
# Gets file extension and looks up the appropriate function to call.
extension = source.suffix[1:]
file_type = _FILE_EXTENSIONS[extension]
name = _LOAD_METHOD(file_type)
try:
return getattr(cls, name)(source)
except AttributeError as error:
message = f'Loading from {file_type} file is not supported'
raise TypeError(message) from error
else:
message = f'settings file {source} not found'
raise FileNotFoundError(message)
@classmethod
def from_ini(cls, source: str | pathlib.Path) -> Settings:
if isinstance(source, str):
source = pathlib.Path(item)
try:
contents = configparser.ConfigParser()
contents.optionxform = lambda option: option
contents.read(source)
except (KeyError, FileNotFoundError) as error:
message = f'settings file {source} not found'
raise FileNotFoundError(message) from error
return cls(contents)
@classmethod
def from_yaml(cls, source: str | pathlib.Path) -> Settings:
if isinstance(source, str):
source = pathlib.Path(item)
try:
with open(source) as config:
contents = yaml.safe_load(config)
except FileNotFoundError as error:
message = f'settings file {path} not found'
raise FileNotFoundError(message) from error
return cls(contents)
The Settings
class inherits the create
class method from wonka.Sourcerer
.
So, the user just needs to pass a file or dict
to the create
classmethod and
it will return a Settings
. So, if your settings are stored in an ini
file,
you just used this code:
Or, if you have the settings in a Python dict
named configuration
, you just
do this: