diff --git a/README.rst b/README.rst index 6cee24b..fb37508 100644 --- a/README.rst +++ b/README.rst @@ -21,7 +21,7 @@ Using .. code-block:: python - from envargs import Var, parse_env + from envargs import Var, parse_env, helpers required_vars = { 'A_INT': Var( @@ -29,7 +29,7 @@ Using validate=lambda x: x >= 0, ), 'A_LIST': Var( - use=lambda x: x.split(','), + use=helpers.split_by(','), validate=( lambda x: len(x) == 2, lambda x: x[0] == 'first element', diff --git a/envargs/__init__.py b/envargs/__init__.py index dd98677..85d86ae 100644 --- a/envargs/__init__.py +++ b/envargs/__init__.py @@ -1,4 +1,5 @@ """Exposed internals of envparse.""" +from . import helpers from .models import Var from .parser import parse_dict from .parser import parse_env diff --git a/envargs/helpers.py b/envargs/helpers.py index c4dad82..9177a50 100644 --- a/envargs/helpers.py +++ b/envargs/helpers.py @@ -1,9 +1,24 @@ -"""Smaller helper functions for taking care of repeated things.""" +"""Default value parsers. Does common things simply.""" -def callables(potential_callables): - """Ensure that the callables are in fact a sequence.""" - if callable(potential_callables): - return [potential_callables] +def boolean(value): + """Return true if the value indicates a truthiness.""" + options = { + 'true', + 't', + '1', + 'yes', + } - return potential_callables + return value.lower() in options + + +def split_by(separator, converter=None): + """Return the value split by commas.""" + def splitter(value): + return [ + converter(each) if converter else each + for each in value.split(',') + ] + + return splitter diff --git a/envargs/models.py b/envargs/models.py index 7ad71b4..c01d7ed 100644 --- a/envargs/models.py +++ b/envargs/models.py @@ -1,6 +1,6 @@ """Everthing about vars.""" from . import errors -from . import helpers +from . import utils class Var: @@ -12,7 +12,7 @@ class Var: def __init__(self, use, validate=None, load_from=None, default=None, err_msg=None): """Return a new instance.""" self.use = use - self.validate_with = helpers.callables(validate) + self.validate_with = utils.callables(validate) self.load_from = load_from self.default = default self.err_msg = err_msg diff --git a/envargs/utils.py b/envargs/utils.py new file mode 100644 index 0000000..c4dad82 --- /dev/null +++ b/envargs/utils.py @@ -0,0 +1,9 @@ +"""Smaller helper functions for taking care of repeated things.""" + + +def callables(potential_callables): + """Ensure that the callables are in fact a sequence.""" + if callable(potential_callables): + return [potential_callables] + + return potential_callables diff --git a/tests/test_funcs.py b/tests/test_funcs.py new file mode 100644 index 0000000..3567f6a --- /dev/null +++ b/tests/test_funcs.py @@ -0,0 +1,39 @@ +"""Testing the default parser functions.""" +import pytest + +from envargs import helpers + + +@pytest.mark.parametrize('case, expected', [ + ('true', True), + ('t', True), + ('True', True), + ('1', True), + ('yes', True), + ('0', False), + ('false', False), + ('no', False), +]) +def test_boolean_parser(case, expected): + """Test the default boolean parser.""" + assert helpers.boolean(case) == expected + + +@pytest.mark.parametrize('case, expected', [ + ('1', ['1']), + ('1,2,3', ['1', '2', '3']), +]) +def test_split_by_comma(case, expected): + """Test the splitter.""" + splitter = helpers.split_by(',') + assert splitter(case) == expected + + +@pytest.mark.parametrize('case, expected', [ + ('1', [1]), + ('1,2,3', [1, 2, 3]), +]) +def test_split_by_comma_and_convert(case, expected): + """Test the splitter.""" + splitter = helpers.split_by(',', converter=int) + assert splitter(case) == expected diff --git a/tests/test_parsing.py b/tests/test_parsing.py index cab0fae..f966c35 100644 --- a/tests/test_parsing.py +++ b/tests/test_parsing.py @@ -4,6 +4,7 @@ from envargs import parse_dict from envargs import parse_env from envargs import Var +from envargs import helpers def test_simple_dict_parsing(): @@ -144,17 +145,9 @@ def test_default_name(): def test_custom_function_boolean(): """Test that we can use a custom function to parse with.""" - def parse_bool(value): - return value.lower() in { - 'true', - 'yes', - 'y', - '1', - } - args = { 'a_bool': Var( - use=parse_bool, + use=helpers.boolean, validate=lambda parsed: isinstance(parsed, bool) ), } @@ -170,15 +163,9 @@ def parse_bool(value): def test_custom_function_int_list(): """Test that we can have more complex parsing functions.""" - def parse_list(value): - return [ - int(each) - for each in value.split(',') - ] - args = { 'a_list': Var( - use=parse_list, + use=helpers.split_by(',', converter=int), validate=( lambda parsed: isinstance(parsed, list), lambda parsed: all(isinstance(each, int) for each in parsed), @@ -199,7 +186,7 @@ def test_complex_defaulting(): """Test that when defaulting, the functions are not used.""" args = { 'a_bool': Var( - use=lambda x: x.lower() in {'1', 't', 'true'}, + use=helpers.boolean, validate=lambda x: isinstance(x, bool), default=False, ), @@ -210,3 +197,21 @@ def test_complex_defaulting(): assert parse_dict(values, args) == { 'a_bool': False, } + + +def test_setting_value(): + """Test parsing with a lambda.""" + args = { + 'a_set': Var( + use=lambda x: set(x.split(',')), + validate=lambda x: isinstance(x, set), + ), + } + + values = { + 'a_set': 'one,two', + } + + assert parse_dict(values, args) == { + 'a_set': {'one', 'two'}, + }