Skip to content

Commit fb096d5

Browse files
committed
Merge branch 'main' into forman-21-append_step
2 parents 0c68eff + ee28dee commit fb096d5

File tree

5 files changed

+54
-8
lines changed

5 files changed

+54
-8
lines changed

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
### Enhancements
44

5+
* It is now possible to reference environment variables
6+
in configuration files using the syntax `${ENV_VAR}`. [#36]
7+
58
* Added a demo Notebook `exmaples/zappend-demo.ipynb` and linked
69
it by a binder badge in README.md. [#47]
710

docs/guide.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ zappend -t output/mycube.zarr -c config.yaml inputs/*.nc
5050
If multiple configuration files are passed, they will be merged into one by
5151
incrementally updating the first by subsequent ones.
5252

53+
!!! info "Environment Variables"
54+
It is possible to include the values of environment variables in JSON or YAML
55+
configuration files using the syntax `${ENV_VAR}` or just `$ENV_VAR`.
56+
5357
You can pass configuration settings to the `zappend` Python function with
5458
the optional `config` keyword argument. Other keyword arguments are
5559
interpreted as individual configuration settings and will be merged into the

tests/test_config.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# https://opensource.org/licenses/MIT.
44

55
import json
6+
import os
67
import unittest
78

89
import fsspec
@@ -83,6 +84,40 @@ def test_normalize_yaml_uri(self):
8384
f.write(yaml.dump(config))
8485
self.assertEqual(config, normalize_config(uri))
8586

87+
def test_interpolate_env_vars_json(self):
88+
uri = "memory://config.json"
89+
config = {
90+
"slice_storage_options": {
91+
"key": "${_TEST_S3_KEY}",
92+
"secret": "$_TEST_S3_SECRET",
93+
}
94+
}
95+
with fsspec.open(uri, "wt") as f:
96+
f.write(json.dumps(config))
97+
os.environ["_TEST_S3_KEY"] = "abc"
98+
os.environ["_TEST_S3_SECRET"] = "123"
99+
self.assertEqual(
100+
{"slice_storage_options": {"key": "abc", "secret": "123"}},
101+
normalize_config(uri),
102+
)
103+
104+
def test_interpolate_env_vars_yaml(self):
105+
uri = "memory://config.yaml"
106+
config = {
107+
"slice_storage_options": {
108+
"key": "${_TEST_S3_KEY}",
109+
"secret": "$_TEST_S3_SECRET",
110+
}
111+
}
112+
with fsspec.open(uri, "w") as f:
113+
f.write(yaml.dump(config))
114+
os.environ["_TEST_S3_KEY"] = "abc"
115+
os.environ["_TEST_S3_SECRET"] = "123"
116+
self.assertEqual(
117+
{"slice_storage_options": {"key": "abc", "secret": 123}},
118+
normalize_config(uri),
119+
)
120+
86121
def test_normalize_file_obj(self):
87122
file_obj = FileObj("memory://config.yaml")
88123
config = {"version": 1, "zarr_version": 2}

zappend/config/config.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
# Permissions are hereby granted under the terms of the MIT License:
33
# https://opensource.org/licenses/MIT.
44

5-
from contextlib import contextmanager
5+
from typing import Any
6+
import contextlib
7+
import io
68
import json
79
import os.path
8-
from typing import Any
10+
import string
911

1012
import jsonschema
1113
import jsonschema.exceptions
@@ -91,10 +93,13 @@ def load_config(config_file: FileObj) -> dict[str, Any]:
9193
logger.info(f"Reading configuration {config_file.uri}")
9294
_, ext = os.path.splitext(config_file.path)
9395
with config_file.fs.open(config_file.path, "rt") as f:
94-
if ext in yaml_extensions:
95-
config = yaml.safe_load(f)
96-
else:
97-
config = json.load(f)
96+
source = f.read()
97+
source = string.Template(source).safe_substitute(os.environ)
98+
stream = io.StringIO(source)
99+
if ext in yaml_extensions:
100+
config = yaml.safe_load(stream)
101+
else:
102+
config = json.load(stream)
98103
if not isinstance(config, dict):
99104
raise TypeError(
100105
f"Invalid configuration:" f" {config_file.uri}: object expected"
@@ -138,6 +143,6 @@ def _merge_values(value_1: Any, value_2: Any) -> Any:
138143
return value_2
139144

140145

141-
@contextmanager
146+
@contextlib.contextmanager
142147
def exclude_from_config(config: dict[str, Any], *keys: str) -> dict[str, Any]:
143148
yield {k: v for k, v in config.items() if k not in keys}

zappend/context.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# Permissions are hereby granted under the terms of the MIT License:
33
# https://opensource.org/licenses/MIT.
44

5-
import inspect
65
import tempfile
76
from typing import Any, Dict
87

0 commit comments

Comments
 (0)