Last active
April 16, 2019 15:45
-
-
Save sloria/55b68b8da8353ed5f547 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import re | |
import urlparse | |
import json as pyjson | |
NOTSET = object() | |
def shortcut(cast, **kwargs): | |
def method(self, var, default=NOTSET): | |
return self.get_value(var, cast=cast, **kwargs) | |
return method | |
class Env(object): | |
BOOLEAN_TRUE_STRINGS = ('true', 'on', 'ok', 'y', 'yes', '1') | |
def __call__(self, var, cast=None, default=NOTSET, parse_default=False): | |
return self.get_value(var, cast=cast, default=default, parse_default=parse_default) | |
# Shortcuts | |
bool = shortcut(bool) | |
str = shortcut(str) | |
int = shortcut(int) | |
float = shortcut(float) | |
json = shortcut(pyjson.loads) | |
def list(self, var, cast=None, default=NOTSET): | |
""" | |
:rtype: list | |
""" | |
return self.get_value(var, cast=list if not cast else [cast], default=default) | |
def tuple(self, var, cast=None, default=NOTSET): | |
""" | |
:rtype: tuple | |
""" | |
return self.get_value(var, cast=tuple if not cast else (cast,), default=default) | |
def dict(self, var, cast=dict, default=NOTSET): | |
""" | |
:rtype: dict | |
""" | |
return self.get_value(var, cast=cast, default=default) | |
def url(self, var, default=NOTSET): | |
""" | |
:rtype: urlparse.ParseResult | |
""" | |
return self.get_value(var, cast=urlparse.urlparse, default=default, parse_default=True) | |
def get_value(self, var, cast=None, default=NOTSET, parse_default=False): | |
"""Return value for given environment variable. | |
:param var: Name of variable. | |
:param cast: Type to cast return value as. | |
:param default: If var not present in environ, return this instead. | |
:param parse_default: force to parse default.. | |
:returns: Value from environment or default (if set) | |
""" | |
try: | |
value = os.environ[var] | |
except KeyError: | |
if default is NOTSET: | |
raise | |
value = default | |
# Resolve any proxied values | |
if hasattr(value, 'startswith') and value.startswith('$'): | |
value = value.lstrip('$') | |
value = self.get_value(value, cast=cast, default=default) | |
if value != default or (parse_default and value): | |
value = self.parse_value(value, cast) | |
return value | |
# Class and static methods | |
@classmethod | |
def parse_value(cls, value, cast): | |
"""Parse and cast provided value | |
:param value: Stringed value. | |
:param cast: Type to cast return value as. | |
:returns: Casted value | |
""" | |
if cast is None: | |
return value | |
elif cast is bool: | |
try: | |
value = int(value) != 0 | |
except ValueError: | |
value = value.lower() in cls.BOOLEAN_TRUE_STRINGS | |
elif isinstance(cast, list): | |
value = list(map(cast[0], [x for x in value.split(',') if x])) | |
elif isinstance(cast, tuple): | |
val = value.strip('(').strip(')').split(',') | |
value = tuple(map(cast[0], [x for x in val if x])) | |
elif isinstance(cast, dict): | |
key_cast = cast.get('key', str) | |
value_cast = cast.get('value', str) | |
value_cast_by_key = cast.get('cast', dict()) | |
value = dict(map( | |
lambda kv: ( | |
key_cast(kv[0]), | |
cls.parse_value(kv[1], value_cast_by_key.get(kv[0], value_cast)) | |
), | |
[val.split('=') for val in value.split(';') if val] | |
)) | |
elif cast is dict: | |
value = dict([val.split('=') for val in value.split(',') if val]) | |
elif cast is list: | |
value = [x for x in value.split(',') if x] | |
elif cast is tuple: | |
val = value.strip('(').strip(')').split(',') | |
value = tuple([x for x in val if x]) | |
elif cast is float: | |
# clean string | |
float_str = re.sub(r'[^\d,\.]', '', value) | |
# split for avoid thousand separator and different locale comma/dot symbol | |
parts = re.split(r'[,\.]', float_str) | |
if len(parts) == 1: | |
float_str = parts[0] | |
else: | |
float_str = "{0}.{1}".format(''.join(parts[0:-1]), parts[-1]) | |
value = float(float_str) | |
else: | |
value = cast(value) | |
return value | |
env = Env() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment