Skip to content

Commit

Permalink
Merge pull request #488 from mattbennett/yaml-env-vars
Browse files Browse the repository at this point in the history
Yaml env vars
  • Loading branch information
mattbennett committed Oct 31, 2017
2 parents 841948c + 763e4b6 commit e41b511
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 9 deletions.
12 changes: 12 additions & 0 deletions docs/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ If you need to quote the values in your YAML file, the explicit ``!env_var`` res
# foobar.yaml
AMQP_URI: !env_var "pyamqp://${RABBITMQ_USER:guest}:${RABBITMQ_PASSWORD:password}@${RABBITMQ_HOST:localhost}"
The environment variable value is interpreted as YAML, so it is possible to use rich types:

.. code-block:: yaml
# foobar.yaml
...
THINGS: ${A_LIST_OF_THINGS}
.. code-block:: shell
$ A_LIST_OF_THINGS=[A,B,C] nameko run --config ./foobar.yaml <module>[:<ServiceClass>]
Interacting with running services
---------------------------------

Expand Down
12 changes: 3 additions & 9 deletions nameko/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,14 @@ def _replace_env_var(match):
return os.environ.get(env_var, default)


def _env_var_constructor(loader, node):
def env_var_constructor(loader, node):
raw_value = loader.construct_scalar(node)
value = ENV_VAR_MATCHER.sub(_replace_env_var, raw_value)
use_implicit_scalar_resolver = True
# PyYAML requires tuple/list value for `implicit` arg in `resolve` method
# containing two items. Second one is not used so passing `None` here.
new_tag = loader.resolve(
yaml.ScalarNode, value, (use_implicit_scalar_resolver, None))
new_node = yaml.ScalarNode(new_tag, value)
return loader.yaml_constructors[new_tag](loader, new_node)
return yaml.safe_load(value)


def setup_yaml_parser():
yaml.add_constructor('!env_var', _env_var_constructor)
yaml.add_constructor('!env_var', env_var_constructor)
yaml.add_implicit_resolver('!env_var', IMPLICIT_ENV_VAR_MATCHER)


Expand Down
102 changes: 102 additions & 0 deletions test/cli/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,90 @@ class TestConfigEnvironmentVariables(object):
""",
{'INT_1': '1', 'INT_2': '2', 'INT_3': '3'},
{'FOO': [1, 2, 3], 'BAR': [1, 2, 3]}
),
# inline list
(
"""
FOO: ${LIST}
BAR:
- 1
- 2
- 3
""",
{"LIST": "[1,2,3]"},
{'FOO': [1, 2, 3], 'BAR': [1, 2, 3]}
),
# inline list with block style
(
"""
FOO: ${LIST}
BAR:
- 1
- 2
- 3
""",
{"LIST": "- 1\n- 2\n- 3"},
{'FOO': [1, 2, 3], 'BAR': [1, 2, 3]}
),
# inline list via explicit tag
(
"""
FOO: !env_var "${LIST}"
BAR:
- 1
- 2
- 3
""",
{"LIST": "[1,2,3]"},
{'FOO': [1, 2, 3], 'BAR': [1, 2, 3]}
),
# inline list with default
(
"""
FOO: ${LIST:[1,2,3]}
BAR:
- 1
- 2
- 3
""",
{},
{'FOO': [1, 2, 3], 'BAR': [1, 2, 3]}
),
# inline list containing list
(
"""
FOO: ${LIST}
BAR:
- 1
- 2
- 3
""",
{"LIST": "[1, 2, 3, ['a', 'b', 'c']]"},
{'FOO': [1, 2, 3, ['a', 'b', 'c']], 'BAR': [1, 2, 3]}
),
# inline dict
(
"""
FOO: ${DICT}
BAR:
- 1
- 2
- 3
""",
{"DICT": "{'one': 1, 'two': 2}"},
{'FOO': {'one': 1, 'two': 2}, 'BAR': [1, 2, 3]}
),
# inline dict with block style
(
"""
FOO: ${DICT}
BAR:
- 1
- 2
- 3
""",
{"DICT": "one: 1\ntwo: 2"},
{'FOO': {'one': 1, 'two': 2}, 'BAR': [1, 2, 3]}
)
])
def test_environment_vars_in_config(
Expand All @@ -135,3 +219,21 @@ def test_environment_vars_in_config(

results = yaml.load(yaml_config)
assert results == expected_config

def test_cannot_recurse(self):

setup_yaml_parser()

yaml_config = """
FOO: ${VAR1}
BAR:
- 1
- 2
- 3
"""

with patch.dict('os.environ'):
os.environ["VAR1"] = "${VAR1}"

results = yaml.load(yaml_config)
assert results == {'FOO': "${VAR1}", 'BAR': [1, 2, 3]}

0 comments on commit e41b511

Please sign in to comment.