cherrydoor/config.py
"""Load configuration."""
__author__ = "opliko"
__license__ = "MIT"
__version__ = "0.8.b0"
__status__ = "Prototype"
import argparse
from uuid import uuid4
from os import environ
import confuse
# add otpional type, because confuse doesn't have it yet
def optional(optional_type, default=None):
"""Create a confuse-compatible type for optional values.
Parameters
----------
optional_type : type
type of the optional value
default : optional
default value if not specified
Returns
-------
template: type
optional type template
"""
template = confuse.as_template(optional_type)
template.default = default
return template
template = {
"version": optional(confuse.OneOf([str, int]), __version__),
"host": str,
"port": optional(int),
"path": optional(confuse.Filename()),
"mongo": {
"url": str,
"name": str,
"username": optional(str),
"password": optional(str),
},
"interface": {
"port": confuse.OneOf([confuse.String(pattern="COM\\d+$"), confuse.Filename()]),
"baudrate": int,
"encoding": optional(str, "utf-8"),
},
"manufacturer_code": confuse.StrSeq(),
"secret_key": optional(str),
"max_session_age": confuse.OneOf([int, None]),
"https": optional(bool, False),
"sentry_dsn": optional(str),
"sentry_csp_url": optional(str),
"log_level": optional(str),
}
config = confuse.LazyConfig("cherrydoor", __name__)
def add_args(parser):
"""Add arguments to the arparse parser.
Parameters
----------
parser : argparse.ArgumentParser
parser to add arguments to
Returns
-------
parser : argparse.ArgumentParser
parser with added arguments
"""
args_template = {
"host": {
"type": str,
"help": "host address used for serving the website",
"dest": "host",
},
"port": {
"type": int,
"help": "port on which the website will be served",
"dest": "port",
},
"mongo-url": {
"type": str,
"help": "url for mongodb instance",
"dest": "mongo.url",
},
"mongo-name": {
"type": str,
"help": "name of the database",
"dest": "mongo.name",
},
"mongo-username": {
"type": str,
"help": "name of a user with at least readwrite permission on the database",
"dest": "mongo.username",
},
"mongo-password": {
"type": str,
"help": "password of a user with at least readwrite permission on the database",
"dest": "mongo.password",
},
"serial-port": {
"type": str,
"help": "port arduino is available on",
"dest": "interface.port",
},
"serial-baudrate": {
"type": str,
"help": "baudrate of the arduino",
"dest": "interface.baudrate",
},
"serial-encoding": {
"type": str,
"help": "encoding used by arduino (default utf-8 is probably the best idea)",
"dest": "interface.encoding",
},
"manufacturer-code": {
"type": list,
"help": "Last two digits of block 0 of cards you want to allow during breaks",
"dest": "manufacturer_code",
},
"secret-key": {
"type": str,
"help": "secret used for csrf and other stuff. Ideally a totally random value",
"dest": "secret_key",
},
"max-session-age": {
"type": int,
"help": "maximum amount of time a session can be active",
"dest": "max_session_age",
},
"https": {
"type": bool,
"help": "whether the app is using https (modifies some security headers)",
"dest": "https",
},
"log-level": {
"type": str,
"help": "Log level shown (defaults to WARN)",
"dest": "log_level",
},
}
config_group = parser.add_argument_group(
title="config", description="configuration options"
)
config_group.set_defaults(env=True, config=None)
config_group.add_argument(
"--no-env",
help="don't use environmental variables for cofiguration",
dest="env",
action="store_false",
)
config_group.add_argument(
"--config",
help="Specify a config file overriding default location",
type=argparse.FileType("r"),
dest="config",
)
for (setting, attributes) in args_template.items():
config_group.add_argument(
f"--{setting}",
help=attributes.get("help", ""),
type=attributes.get("type", str),
dest=attributes.get("dest", None),
)
return parser
def load_config(args=None):
"""
Load configuration from file, environ, or command line arguments.
Parameters
----------
args : argparse.Namespace
arguments from argparse
Returns
-------
config : AttrDict
configuration
"""
if args is not None:
if args.config is not None:
config.set_file(args.config.name)
if args.env:
config.add(load_env())
config.set_args(args, dots=True)
valid_config = config.get(template)
if valid_config.get("secret_key", None) is None:
valid_config["secret_key"] = str(uuid4())
return valid_config, config
def load_env():
"""
Load environmental variables.
Returns
--------
env : dict
environmental variables
"""
return confuse.ConfigSource.of(dict(environ))