Skip to content
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
2e279c8
Add CLI option
maddenp-cu Sep 15, 2025
1b9560f
Add comment
maddenp-cu Sep 15, 2025
c99c855
CLI updates
maddenp-cu Sep 15, 2025
c78945a
WIP
maddenp-cu Sep 15, 2025
e7ce646
WIP
maddenp-cu Sep 15, 2025
a9569c1
Plumbing
maddenp-cu Sep 15, 2025
ef8229e
Update docs
maddenp-cu Sep 15, 2025
a6e210c
Merge branch 'main' into gh-792-config-compose
maddenp-cu Sep 15, 2025
80eb2eb
Update docs
maddenp-cu Sep 16, 2025
18c5b80
Add test
maddenp-cu Sep 16, 2025
594de1a
WIP
maddenp-cu Sep 16, 2025
e9a4f65
WIP
maddenp-cu Sep 16, 2025
207b968
realize_config -> realize
maddenp-cu Sep 16, 2025
2f20915
Work on dynamic docstring
maddenp-cu Sep 16, 2025
21130e8
Work on dynamic docstring
maddenp-cu Sep 16, 2025
d88f271
WIP
maddenp-cu Sep 16, 2025
1f296e2
WIP
maddenp-cu Sep 16, 2025
8126013
WIP
maddenp-cu Sep 16, 2025
d17400c
Merge branch 'main' into gh-792-config-compose
maddenp-cu Sep 16, 2025
f6d6a6b
Post-merge fixup
maddenp-cu Sep 16, 2025
467ef0c
WIP
maddenp-cu Sep 16, 2025
3c6ee1e
WIP
maddenp-cu Sep 16, 2025
0799f14
Work on tests
maddenp-cu Sep 16, 2025
be0234a
Work on tests
maddenp-cu Sep 16, 2025
787f70e
Work on tests
maddenp-cu Sep 16, 2025
33a830d
Work on tests
maddenp-cu Sep 16, 2025
ca51080
Work on tests
maddenp-cu Sep 16, 2025
f0a657c
Work on tests
maddenp-cu Sep 16, 2025
fae9c71
Work on tests
maddenp-cu Sep 16, 2025
ec246f0
Work on tests
maddenp-cu Sep 16, 2025
c26f53b
Work on docs
maddenp-cu Sep 16, 2025
b0bf44b
Fix docstrings
maddenp-cu Sep 16, 2025
b9e4b99
Work on docs
maddenp-cu Sep 16, 2025
fd8b84c
Work on docs
maddenp-cu Sep 16, 2025
2db1695
Work on docs
maddenp-cu Sep 17, 2025
9f93fbc
Update docs
maddenp-cu Sep 17, 2025
1eca86b
Fix typo
maddenp-cu Sep 17, 2025
91dd235
Update docs
maddenp-cu Sep 17, 2025
68caa5f
Use more regular keys/vals in unit test
maddenp-cu Sep 17, 2025
7ceefc9
Implement --realize
maddenp-cu Sep 17, 2025
003c63e
Update custom.css
maddenp-cu Sep 17, 2025
94dc19a
Merge branch 'main' into gh-792-config-compose
maddenp-cu Sep 17, 2025
7b714bc
Update docstring
maddenp-cu Sep 17, 2025
db78823
Update 'render' description
maddenp-cu Sep 17, 2025
cdc7eb8
Update docs
maddenp-cu Sep 17, 2025
beaf3f9
Update docs/index.rst
maddenp-cu Sep 17, 2025
06df163
Update src/uwtools/api/config.py
maddenp-cu Sep 17, 2025
8613b26
Add value to override in compose-base.yaml
maddenp-cu Sep 17, 2025
c5ef0a9
Update test
maddenp-cu Sep 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ When the Linux diff tool just doesn't work for comparing unordered namelists wit

| :any:`CLI documentation with examples<cli_config_compare_examples>`

Compose Action
""""""""""""""

To compose configs is to start with a base config and to update its values, structurally, from the contents of one or more other configs of the same type (YAML, Fortran namelist, etc.). Composition supports building up complex experiment configurations by, for example, combining default values appropriate to the overall application with values needed on a particular machine, and then with specific user requrements, each stored in their own config files. Such a hierarchical approach to configuration management provides flexibility and avoids repetition of common values across files.

| :any:`CLI documentation with examples<cli_config_compose_examples>`

Realize Action
""""""""""""""

Expand Down
44 changes: 44 additions & 0 deletions docs/sections/user_guide/cli/tools/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,50 @@ The examples that follow use identical namelist files ``a.nml`` and ``b.nml`` wi
.. literalinclude:: config/compare-format-mismatch.out
:language: text

.. _cli_config_compose_examples:

``compose``
-----------

The ``compose`` action builds up a final config by repeatedly updating a base config with the contents of other configs of the same format.

.. literalinclude:: config/compose-help.cmd
:language: text
:emphasize-lines: 1
.. literalinclude:: config/compose-help.out
:language: text

Examples
^^^^^^^^

* Consider three YAML configs:

.. literalinclude:: config/compose-base.yaml
:caption: compose-base.yaml
:language: yaml
.. literalinclude:: config/compose-update-1.yaml
:caption: compose-update-1.yaml
:language: yaml
.. literalinclude:: config/compose-update-2.yaml
:caption: compose-update-2.yaml
:language: yaml

Compose the three together, writing to ``stdout``:

.. literalinclude:: config/compose-1.cmd
:language: text
:emphasize-lines: 1
.. literalinclude:: config/compose-1.out
:language: yaml

Values provided by update configs override or augment values provided in the base config, while unaffected values survive to the final config. Priority of values increases from left to right.

Additionally:

* Configs in the ``ini``, ``nml``, and ``sh`` formats can be similarly composed.
* The ``--input-config`` and ``--output-config`` options can be used to specify the format of the input and output configs, respectively, for cases when ``uwtools`` cannot deduce the format of configs from their filename extensions. The the formats are neither explicitly provided or deduced, ``yaml`` is assumed.
* The ``--output-file`` / ``-o`` option can be added to direct the output to a file.

.. _cli_config_realize_examples:

``realize``
Expand Down
2 changes: 2 additions & 0 deletions docs/sections/user_guide/cli/tools/config/compose-1.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
uw config compose compose-base.yaml compose-update-1.yaml compose-update-2.yaml

5 changes: 5 additions & 0 deletions docs/sections/user_guide/cli/tools/config/compose-1.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
constants:
pi: 3.142
e: 2.718
color: blue
flower: rose
3 changes: 3 additions & 0 deletions docs/sections/user_guide/cli/tools/config/compose-base.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
constants:
pi: 3.142
color: blue
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uw config compose --help
25 changes: 25 additions & 0 deletions docs/sections/user_guide/cli/tools/config/compose-help.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
usage: uw config compose [-h] [--version] [--input-format {ini,nml,sh,yaml}]
[--output-format {ini,nml,sh,yaml}]
[--output-file PATH] [--quiet] [--verbose]
CONFIG [CONFIG ...]

Compose configs

positional arguments:
CONFIG

Optional arguments:
-h, --help
Show help and exit
--version
Show version info and exit
--input-format {ini,nml,sh,yaml}
Input format (default: yaml)
--output-format {ini,nml,sh,yaml}
Output format (default: yaml)
--output-file PATH, -o PATH
Path to output file (default: write to stdout)
--quiet, -q
Print no logging messages
--verbose, -v
Print all logging messages
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
constants:
e: 2.718
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
flower: rose
2 changes: 2 additions & 0 deletions docs/sections/user_guide/cli/tools/config/help.out
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Positional arguments:
ACTION
compare
Compare configs
compose
Compose configs
realize
Realize config
validate
Expand Down
10 changes: 5 additions & 5 deletions docs/sections/user_guide/cli/tools/config/realize-help.out
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ Optional arguments:
--version
Show version info and exit
--input-file PATH, -i PATH
Path to input file (defaults to stdin)
Path to input file (default: read from stdin)
--input-format {ini,nml,sh,yaml}
Input format
Input format (default: yaml)
--update-file PATH, -u PATH
Path to update file (defaults to stdin)
Path to update file (default: read from stdin)
--update-format {ini,nml,sh,yaml}
Update format
--output-file PATH, -o PATH
Path to output file (defaults to stdout)
Path to output file (default: write to stdout)
--output-format {ini,nml,sh,yaml}
Output format
Output format (default: yaml)
--key-path KEY[.KEY...]
Dot-separated path of keys to the block to be output
--values-needed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Optional arguments:
--version
Show version info and exit
--input-file PATH, -i PATH
Path to input file (defaults to stdin)
Path to input file (default: read from stdin)
--quiet, -q
Print no logging messages
--verbose, -v
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Optional arguments:
--config-file PATH, -c PATH
Path to UW YAML config file (default: read from stdin)
--output-file PATH, -o PATH
Path to output file (defaults to stdout)
Path to output file (default: write to stdout)
--quiet, -q
Print no logging messages
--verbose, -v
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Optional arguments:
--version
Show version info and exit
--input-file PATH, -i PATH
Path to input file (defaults to stdin)
Path to input file (default: read from stdin)
--quiet, -q
Print no logging messages
--verbose, -v
Expand Down
4 changes: 2 additions & 2 deletions docs/sections/user_guide/cli/tools/template/render-help.out
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ Optional arguments:
--version
Show version info and exit
--input-file PATH, -i PATH
Path to input file (defaults to stdin)
Path to input file (default: read from stdin)
--output-file PATH, -o PATH
Path to output file (defaults to stdout)
Path to output file (default: write to stdout)
--values-file PATH
Path to file providing override or interpolation values
--values-format {ini,nml,sh,yaml}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ Optional arguments:
--version
Show version info and exit
--input-file PATH, -i PATH
Path to input file (defaults to stdin)
Path to input file (default: read from stdin)
--output-file PATH, -o PATH
Path to output file (defaults to stdout)
Path to output file (default: write to stdout)
--dry-run
Only log info, making no changes
--quiet, -q
Expand Down
10 changes: 10 additions & 0 deletions docs/static/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,13 @@ html.writer-html5 .rst-content table.docutils td>p {
.wy-table-responsive table td, .wy-table-responsive table th {
white-space: inherit;
}

/* Left align captions. */

.rst-content .caption-number {
display: none;
}

.rst-content .code-block-caption {
text-align: left;
}
53 changes: 40 additions & 13 deletions src/uwtools/api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
from uwtools.config.formats.nml import NMLConfig
from uwtools.config.formats.sh import SHConfig
from uwtools.config.formats.yaml import YAMLConfig
from uwtools.config.tools import compare_configs as _compare
from uwtools.config.tools import realize_config as _realize
from uwtools.config.tools import compare as _compare
from uwtools.config.tools import compose as _compose
from uwtools.config.tools import realize as _realize
from uwtools.config.validator import ConfigDataT, ConfigPathT
from uwtools.config.validator import validate_check_config as _validate_check_config
from uwtools.config.validator import validate_external as _validate_external
Expand All @@ -31,19 +32,28 @@


def compare(
path1: Path | str,
path2: Path | str,
format1: str | None = None,
format2: str | None = None,
path1: Path | str, path2: Path | str, format1: str | None = None, format2: str | None = None
) -> bool:
"""
NB: This docstring is dynamically replaced: See compare.__doc__ definition below.
"""
return _compare(
path1=Path(path1),
path2=Path(path2),
format1=format1,
format2=format2,
return _compare(path1=Path(path1), path2=Path(path2), format1=format1, format2=format2)


def compose(
configs: list[str | Path],
output_file: Path | str | None = None,
input_format: str | None = None,
output_format: str | None = None,
) -> bool:
"""
NB: This docstring is dynamically replaced: See compose.__doc__ definition below.
"""
return _compose(
configs=list(map(Path, configs)),
output_file=Path(output_file) if output_file else None,
input_format=input_format,
output_format=output_format,
)


Expand Down Expand Up @@ -217,8 +227,24 @@ def validate(
:param format1: Format of 1st config file (optional if file's extension is recognized).
:param format2: Format of 2nd config file (optional if file's extension is recognized).
:return: ``False`` if config files had differences, otherwise ``True``.
""".format(extensions=", ".join(_FORMAT.extensions())).strip()
""".format(extensions=", ".join([f"``{x}``" for x in _FORMAT.extensions()])).strip()

compose.__doc__ = """
Compose config files.

Specify explicit input or output formats to override default treatment baseed on file extension.
Recognized file extensions are: {extensions}.

:param configs: Paths to configs to compose.
:param output_file: Output config destination (default: write to ``stdout``).
:param input_format: Format of configs to compose (choices: {choices}, default: ``{default}``)
:param output_format: Format of output config (choices: {choices}, default: ``{default}``)
:return: ``True`` if no errors were encountered.
""".format(
default=_FORMAT.yaml,
extensions=", ".join([f"``{x}``" for x in _FORMAT.extensions()]),
choices=", ".join([f"``{x}``" for x in (_FORMAT.ini, _FORMAT.nml, _FORMAT.sh, _FORMAT.yaml)]),
).strip()

realize.__doc__ = """
Realize a config based on a base input config and an optional update config.
Expand Down Expand Up @@ -258,7 +284,7 @@ def validate(
:param stdin_ok: OK to read from ``stdin``?
:return: The ``dict`` representation of the realized config.
:raises: ``UWConfigRealizeError`` if ``total`` is ``True`` and any Jinja2 syntax was not rendered.
""".format(extensions=", ".join(_FORMAT.extensions())).strip() # noqa: E501
""".format(extensions=", ".join([f"``{x}``" for x in _FORMAT.extensions()])).strip() # noqa: E501

__all__ = [
"Config",
Expand All @@ -268,6 +294,7 @@ def validate(
"SHConfig",
"YAMLConfig",
"compare",
"compose",
"get_fieldtable_config",
"get_ini_config",
"get_nml_config",
Expand Down
Loading