diff --git a/docs/sections/user_guide/cli/drivers/.gitignore b/docs/sections/user_guide/cli/drivers/.gitignore new file mode 100644 index 000000000..1c12cf9bd --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/.gitignore @@ -0,0 +1 @@ +schema diff --git a/docs/sections/user_guide/cli/drivers/cdeps.rst b/docs/sections/user_guide/cli/drivers/cdeps.rst index c2e98694d..1044ad6c6 100644 --- a/docs/sections/user_guide/cli/drivers/cdeps.rst +++ b/docs/sections/user_guide/cli/drivers/cdeps.rst @@ -37,7 +37,6 @@ Its contents are described in depth in section :ref:`cdeps_yaml`. Each of the va The driver creates a ``datm_in`` Fortran namelist file and a ``datm.streams`` stream-configuration file in the directory specified by ``rundir:`` in the config. - * Specifying the ``--dry-run`` flag results in the driver logging messages about actions it would have taken, without actually taking any. .. code-block:: text @@ -45,3 +44,11 @@ Its contents are described in depth in section :ref:`cdeps_yaml`. Each of the va $ uw cdeps run --config-file config.yaml --cycle 2023-12-15T18 --batch --dry-run .. include:: /shared/key_path.rst + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: cdeps/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: cdeps/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/cdeps/help.out b/docs/sections/user_guide/cli/drivers/cdeps/help.out index fc97c9412..8fdcbbf8e 100644 --- a/docs/sections/user_guide/cli/drivers/cdeps/help.out +++ b/docs/sections/user_guide/cli/drivers/cdeps/help.out @@ -1,4 +1,4 @@ -usage: uw cdeps [-h] [--version] TASK ... +usage: uw cdeps [-h] [--version] [--show-schema] TASK ... Execute cdeps tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/cdeps/show-schema.cmd b/docs/sections/user_guide/cli/drivers/cdeps/show-schema.cmd new file mode 100644 index 000000000..5be54d6cd --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/cdeps/show-schema.cmd @@ -0,0 +1,2 @@ +uw cdeps --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/cdeps/show-schema.out b/docs/sections/user_guide/cli/drivers/cdeps/show-schema.out new file mode 100644 index 000000000..b5e4a7c03 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/cdeps/show-schema.out @@ -0,0 +1,21 @@ +{ + "$defs": { + "streams": { + "additionalProperties": false, + "minProperties": 1, + "patternProperties": { + "^stream0[1-9]$": { + "additionalProperties": false, + "properties": { + "dtlimit": { +... + "required": [ + "rundir" + ] + } + }, + "required": [ + "cdeps" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/chgres_cube.rst b/docs/sections/user_guide/cli/drivers/chgres_cube.rst index 61f55661f..6d954a1c6 100644 --- a/docs/sections/user_guide/cli/drivers/chgres_cube.rst +++ b/docs/sections/user_guide/cli/drivers/chgres_cube.rst @@ -58,3 +58,11 @@ Its contents are described in depth in section :ref:`chgres_cube_yaml`. Each of .. code-block:: text $ uw chgres_cube provisioned_rundir --config-file config.yaml --cycle 2023-12-15T18 --batch + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: chgres_cube/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: chgres_cube/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/chgres_cube/help.out b/docs/sections/user_guide/cli/drivers/chgres_cube/help.out index 11879ab3c..7d632bb8b 100644 --- a/docs/sections/user_guide/cli/drivers/chgres_cube/help.out +++ b/docs/sections/user_guide/cli/drivers/chgres_cube/help.out @@ -1,4 +1,4 @@ -usage: uw chgres_cube [-h] [--version] TASK ... +usage: uw chgres_cube [-h] [--version] [--show-schema] TASK ... Execute chgres_cube tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/chgres_cube/show-schema.cmd b/docs/sections/user_guide/cli/drivers/chgres_cube/show-schema.cmd new file mode 100644 index 000000000..f539df7cc --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/chgres_cube/show-schema.cmd @@ -0,0 +1,2 @@ +uw chgres_cube --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/chgres_cube/show-schema.out b/docs/sections/user_guide/cli/drivers/chgres_cube/show-schema.out new file mode 100644 index 000000000..97d8138dd --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/chgres_cube/show-schema.out @@ -0,0 +1,21 @@ +{ + "properties": { + "chgres_cube": { + "additionalProperties": false, + "properties": { + "execution": { + "additionalProperties": false, + "properties": { + "batchargs": { + "additionalProperties": true, +... + "rundir" + ], + "type": "object" + } + }, + "required": [ + "chgres_cube" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/esg_grid.rst b/docs/sections/user_guide/cli/drivers/esg_grid.rst index 7bacfb75a..e04e8af80 100644 --- a/docs/sections/user_guide/cli/drivers/esg_grid.rst +++ b/docs/sections/user_guide/cli/drivers/esg_grid.rst @@ -58,3 +58,11 @@ The driver creates a ``runscript.esg_grid`` file in the directory specified by ` .. code-block:: text $ uw esg_grid provisioned_rundir --config-file config.yaml --batch + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: esg_grid/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: esg_grid/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/esg_grid/help.out b/docs/sections/user_guide/cli/drivers/esg_grid/help.out index 01a1825fe..0069553e6 100644 --- a/docs/sections/user_guide/cli/drivers/esg_grid/help.out +++ b/docs/sections/user_guide/cli/drivers/esg_grid/help.out @@ -1,4 +1,4 @@ -usage: uw esg_grid [-h] [--version] TASK ... +usage: uw esg_grid [-h] [--version] [--show-schema] TASK ... Execute esg_grid tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/esg_grid/show-schema.cmd b/docs/sections/user_guide/cli/drivers/esg_grid/show-schema.cmd new file mode 100644 index 000000000..a98ee17dc --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/esg_grid/show-schema.cmd @@ -0,0 +1,2 @@ +uw esg_grid --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/esg_grid/show-schema.out b/docs/sections/user_guide/cli/drivers/esg_grid/show-schema.out new file mode 100644 index 000000000..8ac2cdd16 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/esg_grid/show-schema.out @@ -0,0 +1,21 @@ +{ + "$defs": { + "base_file": { + "type": "string" + }, + "namelist_content": { + "additionalproperties": false, + "properties": { + "regional_grid_nml": { + "additionalProperties": false, +... + "rundir" + ], + "type": "object" + } + }, + "required": [ + "esg_grid" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/filter_topo.rst b/docs/sections/user_guide/cli/drivers/filter_topo.rst index 1787c9125..a7a77ff5d 100644 --- a/docs/sections/user_guide/cli/drivers/filter_topo.rst +++ b/docs/sections/user_guide/cli/drivers/filter_topo.rst @@ -52,3 +52,11 @@ Its contents are described in section :ref:`filter_topo_yaml`. $ uw filter_topo run --config-file config.yaml --batch --dry-run .. include:: /shared/key_path.rst + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: filter_topo/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: filter_topo/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/filter_topo/help.out b/docs/sections/user_guide/cli/drivers/filter_topo/help.out index a1fe57f9f..1c426c0f4 100644 --- a/docs/sections/user_guide/cli/drivers/filter_topo/help.out +++ b/docs/sections/user_guide/cli/drivers/filter_topo/help.out @@ -1,4 +1,4 @@ -usage: uw filter_topo [-h] [--version] TASK ... +usage: uw filter_topo [-h] [--version] [--show-schema] TASK ... Execute filter_topo tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/filter_topo/show-schema.cmd b/docs/sections/user_guide/cli/drivers/filter_topo/show-schema.cmd new file mode 100644 index 000000000..43883c9fa --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/filter_topo/show-schema.cmd @@ -0,0 +1,2 @@ +uw filter_topo --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/filter_topo/show-schema.out b/docs/sections/user_guide/cli/drivers/filter_topo/show-schema.out new file mode 100644 index 000000000..d1ff44ffc --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/filter_topo/show-schema.out @@ -0,0 +1,21 @@ +{ + "properties": { + "filter_topo": { + "additionalProperties": false, + "properties": { + "config": { + "additionalProperties": false, + "properties": { + "input_grid_file": { + "type": "string" +... + "rundir" + ], + "type": "object" + } + }, + "required": [ + "filter_topo" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/fv3.rst b/docs/sections/user_guide/cli/drivers/fv3.rst index 2e1e2d858..c601d7464 100644 --- a/docs/sections/user_guide/cli/drivers/fv3.rst +++ b/docs/sections/user_guide/cli/drivers/fv3.rst @@ -53,3 +53,11 @@ The examples use a configuration file named ``config.yaml``. Its contents are de .. code-block:: text $ uw fv3 provisioned_rundir --config-file config.yaml --cycle 2024-02-11T12 --batch + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: fv3/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: fv3/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/fv3/help.out b/docs/sections/user_guide/cli/drivers/fv3/help.out index e2fd2adf2..09420ef44 100644 --- a/docs/sections/user_guide/cli/drivers/fv3/help.out +++ b/docs/sections/user_guide/cli/drivers/fv3/help.out @@ -1,4 +1,4 @@ -usage: uw fv3 [-h] [--version] TASK ... +usage: uw fv3 [-h] [--version] [--show-schema] TASK ... Execute fv3 tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/fv3/show-schema.cmd b/docs/sections/user_guide/cli/drivers/fv3/show-schema.cmd new file mode 100644 index 000000000..25d915cea --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/fv3/show-schema.cmd @@ -0,0 +1,2 @@ +uw fv3 --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/fv3/show-schema.out b/docs/sections/user_guide/cli/drivers/fv3/show-schema.out new file mode 100644 index 000000000..6957302c1 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/fv3/show-schema.out @@ -0,0 +1,21 @@ +{ + "properties": { + "fv3": { + "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "domain": { + "const": "regional" +... + "rundir" + ], + "type": "object" + } + }, + "required": [ + "fv3" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/global_equiv_resol.rst b/docs/sections/user_guide/cli/drivers/global_equiv_resol.rst index f6a189727..f02e99760 100644 --- a/docs/sections/user_guide/cli/drivers/global_equiv_resol.rst +++ b/docs/sections/user_guide/cli/drivers/global_equiv_resol.rst @@ -52,3 +52,11 @@ Its contents are described in section :ref:`global_equiv_resol_yaml`. $ uw global_equiv_resol run --config-file config.yaml --batch --dry-run .. include:: /shared/key_path.rst + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: global_equiv_resol/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: global_equiv_resol/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/global_equiv_resol/help.out b/docs/sections/user_guide/cli/drivers/global_equiv_resol/help.out index 227ad0499..eb3d7b505 100644 --- a/docs/sections/user_guide/cli/drivers/global_equiv_resol/help.out +++ b/docs/sections/user_guide/cli/drivers/global_equiv_resol/help.out @@ -1,4 +1,4 @@ -usage: uw global_equiv_resol [-h] [--version] TASK ... +usage: uw global_equiv_resol [-h] [--version] [--show-schema] TASK ... Execute global_equiv_resol tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/global_equiv_resol/show-schema.cmd b/docs/sections/user_guide/cli/drivers/global_equiv_resol/show-schema.cmd new file mode 100644 index 000000000..6d43fbfb2 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/global_equiv_resol/show-schema.cmd @@ -0,0 +1,2 @@ +uw global_equiv_resol --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/global_equiv_resol/show-schema.out b/docs/sections/user_guide/cli/drivers/global_equiv_resol/show-schema.out new file mode 100644 index 000000000..bfdd2e0e0 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/global_equiv_resol/show-schema.out @@ -0,0 +1,21 @@ +{ + "properties": { + "global_equiv_resol": { + "additionalProperties": false, + "properties": { + "execution": { + "additionalProperties": false, + "properties": { + "batchargs": { + "additionalProperties": true, +... + "rundir" + ], + "type": "object" + } + }, + "required": [ + "global_equiv_resol" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/ioda.rst b/docs/sections/user_guide/cli/drivers/ioda.rst index 132337a44..9ea6e745c 100644 --- a/docs/sections/user_guide/cli/drivers/ioda.rst +++ b/docs/sections/user_guide/cli/drivers/ioda.rst @@ -52,3 +52,11 @@ The driver creates a ``runscript.ioda`` file in the directory specified by ``run $ uw ioda run --config-file config.yaml --cycle 2024-05-22T12 --batch --dry-run .. include:: /shared/key_path.rst + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: ioda/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: ioda/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/ioda/help.out b/docs/sections/user_guide/cli/drivers/ioda/help.out index f7486be93..692138971 100644 --- a/docs/sections/user_guide/cli/drivers/ioda/help.out +++ b/docs/sections/user_guide/cli/drivers/ioda/help.out @@ -1,4 +1,4 @@ -usage: uw ioda [-h] [--version] TASK ... +usage: uw ioda [-h] [--version] [--show-schema] TASK ... Execute ioda tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/ioda/show-schema.cmd b/docs/sections/user_guide/cli/drivers/ioda/show-schema.cmd new file mode 100644 index 000000000..1b16cbe71 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/ioda/show-schema.cmd @@ -0,0 +1,2 @@ +uw ioda --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/ioda/show-schema.out b/docs/sections/user_guide/cli/drivers/ioda/show-schema.out new file mode 100644 index 000000000..f90bcf6a7 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/ioda/show-schema.out @@ -0,0 +1,21 @@ +{ + "properties": { + "ioda": { + "additionalProperties": false, + "properties": { + "configuration_file": { + "additionalProperties": false, + "anyOf": [ + { + "required": [ +... + "rundir" + ], + "type": "object" + } + }, + "required": [ + "ioda" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/jedi.rst b/docs/sections/user_guide/cli/drivers/jedi.rst index 1aa90992f..464300b94 100644 --- a/docs/sections/user_guide/cli/drivers/jedi.rst +++ b/docs/sections/user_guide/cli/drivers/jedi.rst @@ -52,3 +52,11 @@ The driver creates a ``runscript.jedi`` file in the directory specified by ``run $ uw jedi run --config-file config.yaml --cycle 2024-05-22T12 --batch --dry-run .. include:: /shared/key_path.rst + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: jedi/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: jedi/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/jedi/help.out b/docs/sections/user_guide/cli/drivers/jedi/help.out index f35898de0..fe3d01ae0 100644 --- a/docs/sections/user_guide/cli/drivers/jedi/help.out +++ b/docs/sections/user_guide/cli/drivers/jedi/help.out @@ -1,4 +1,4 @@ -usage: uw jedi [-h] [--version] TASK ... +usage: uw jedi [-h] [--version] [--show-schema] TASK ... Execute jedi tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/jedi/show-schema.cmd b/docs/sections/user_guide/cli/drivers/jedi/show-schema.cmd new file mode 100644 index 000000000..0567b841e --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/jedi/show-schema.cmd @@ -0,0 +1,2 @@ +uw jedi --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/jedi/show-schema.out b/docs/sections/user_guide/cli/drivers/jedi/show-schema.out new file mode 100644 index 000000000..2dba61813 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/jedi/show-schema.out @@ -0,0 +1,21 @@ +{ + "properties": { + "jedi": { + "additionalProperties": false, + "properties": { + "configuration_file": { + "additionalProperties": false, + "anyOf": [ + { + "required": [ +... + "rundir" + ], + "type": "object" + } + }, + "required": [ + "jedi" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/make_hgrid.rst b/docs/sections/user_guide/cli/drivers/make_hgrid.rst index 8d38fd152..dc6d79dd3 100644 --- a/docs/sections/user_guide/cli/drivers/make_hgrid.rst +++ b/docs/sections/user_guide/cli/drivers/make_hgrid.rst @@ -60,3 +60,11 @@ Its contents are described in section :ref:`make_hgrid_yaml`. $ uw make_hgrid run --config-file config.yaml --batch --dry-run .. include:: /shared/key_path.rst + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: make_hgrid/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: make_hgrid/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/make_hgrid/help.out b/docs/sections/user_guide/cli/drivers/make_hgrid/help.out index c61016726..7acf294ca 100644 --- a/docs/sections/user_guide/cli/drivers/make_hgrid/help.out +++ b/docs/sections/user_guide/cli/drivers/make_hgrid/help.out @@ -1,4 +1,4 @@ -usage: uw make_hgrid [-h] [--version] TASK ... +usage: uw make_hgrid [-h] [--version] [--show-schema] TASK ... Execute make_hgrid tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/make_hgrid/show-schema.cmd b/docs/sections/user_guide/cli/drivers/make_hgrid/show-schema.cmd new file mode 100644 index 000000000..e41d2138c --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/make_hgrid/show-schema.cmd @@ -0,0 +1,2 @@ +uw make_hgrid --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/make_hgrid/show-schema.out b/docs/sections/user_guide/cli/drivers/make_hgrid/show-schema.out new file mode 100644 index 000000000..181977dd5 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/make_hgrid/show-schema.out @@ -0,0 +1,21 @@ +{ + "properties": { + "make_hgrid": { + "additionalProperties": false, + "properties": { + "config": { + "additionalProperties": false, + "allOf": [ + { + "if": { +... + "execution", + "rundir" + ] + } + }, + "required": [ + "make_hgrid" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/make_solo_mosaic.rst b/docs/sections/user_guide/cli/drivers/make_solo_mosaic.rst index d3c95e77b..7d110acad 100644 --- a/docs/sections/user_guide/cli/drivers/make_solo_mosaic.rst +++ b/docs/sections/user_guide/cli/drivers/make_solo_mosaic.rst @@ -61,3 +61,11 @@ Its contents are described in section :ref:`make_solo_mosaic_yaml`. $ uw make_solo_mosaic run --config-file config.yaml --batch --dry-run .. include:: /shared/key_path.rst + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: make_solo_mosaic/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: make_solo_mosaic/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/make_solo_mosaic/help.out b/docs/sections/user_guide/cli/drivers/make_solo_mosaic/help.out index 1af7ff17e..0b899c013 100644 --- a/docs/sections/user_guide/cli/drivers/make_solo_mosaic/help.out +++ b/docs/sections/user_guide/cli/drivers/make_solo_mosaic/help.out @@ -1,4 +1,4 @@ -usage: uw make_solo_mosaic [-h] [--version] TASK ... +usage: uw make_solo_mosaic [-h] [--version] [--show-schema] TASK ... Execute make_solo_mosaic tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/make_solo_mosaic/show-schema.cmd b/docs/sections/user_guide/cli/drivers/make_solo_mosaic/show-schema.cmd new file mode 100644 index 000000000..8100c4a37 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/make_solo_mosaic/show-schema.cmd @@ -0,0 +1,2 @@ +uw make_solo_mosaic --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/make_solo_mosaic/show-schema.out b/docs/sections/user_guide/cli/drivers/make_solo_mosaic/show-schema.out new file mode 100644 index 000000000..61d30fd75 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/make_solo_mosaic/show-schema.out @@ -0,0 +1,21 @@ +{ + "properties": { + "make_solo_mosaic": { + "additionalProperties": false, + "properties": { + "config": { + "additionalProperties": false, + "properties": { + "dir": { + "type": "string" +... + "rundir" + ], + "type": "object" + } + }, + "required": [ + "make_solo_mosaic" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/mpas.rst b/docs/sections/user_guide/cli/drivers/mpas.rst index 671d019a0..090d50a92 100644 --- a/docs/sections/user_guide/cli/drivers/mpas.rst +++ b/docs/sections/user_guide/cli/drivers/mpas.rst @@ -58,3 +58,11 @@ Its contents are described in depth in section :ref:`mpas_yaml`. .. code-block:: text $ uw mpas provisioned_rundir --config-file config.yaml --cycle 2025-02-12T12 --batch + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: mpas/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: mpas/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/mpas/help.out b/docs/sections/user_guide/cli/drivers/mpas/help.out index 000018cdf..cb7106e5f 100644 --- a/docs/sections/user_guide/cli/drivers/mpas/help.out +++ b/docs/sections/user_guide/cli/drivers/mpas/help.out @@ -1,4 +1,4 @@ -usage: uw mpas [-h] [--version] TASK ... +usage: uw mpas [-h] [--version] [--show-schema] TASK ... Execute mpas tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/mpas/show-schema.cmd b/docs/sections/user_guide/cli/drivers/mpas/show-schema.cmd new file mode 100644 index 000000000..d1293fe18 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/mpas/show-schema.cmd @@ -0,0 +1,2 @@ +uw mpas --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/mpas/show-schema.out b/docs/sections/user_guide/cli/drivers/mpas/show-schema.out new file mode 100644 index 000000000..561d89def --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/mpas/show-schema.out @@ -0,0 +1,21 @@ +{ + "properties": { + "mpas": { + "additionalProperties": false, + "properties": { + "execution": { + "additionalProperties": false, + "properties": { + "batchargs": { + "additionalProperties": true, +... + "streams" + ], + "type": "object" + } + }, + "required": [ + "mpas" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/mpas_init.rst b/docs/sections/user_guide/cli/drivers/mpas_init.rst index 30be881e4..b8286a929 100644 --- a/docs/sections/user_guide/cli/drivers/mpas_init.rst +++ b/docs/sections/user_guide/cli/drivers/mpas_init.rst @@ -58,3 +58,11 @@ Its contents are described in depth in section :ref:`mpas_init_yaml`. .. code-block:: text $ uw mpas_init provisioned_rundir --config-file config.yaml --cycle 2023-12-18T00 --batch + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: mpas_init/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: mpas_init/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/mpas_init/help.out b/docs/sections/user_guide/cli/drivers/mpas_init/help.out index 7e51e71d4..c2f743422 100644 --- a/docs/sections/user_guide/cli/drivers/mpas_init/help.out +++ b/docs/sections/user_guide/cli/drivers/mpas_init/help.out @@ -1,4 +1,4 @@ -usage: uw mpas_init [-h] [--version] TASK ... +usage: uw mpas_init [-h] [--version] [--show-schema] TASK ... Execute mpas_init tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/mpas_init/show-schema.cmd b/docs/sections/user_guide/cli/drivers/mpas_init/show-schema.cmd new file mode 100644 index 000000000..32253d17d --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/mpas_init/show-schema.cmd @@ -0,0 +1,2 @@ +uw mpas_init --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/mpas_init/show-schema.out b/docs/sections/user_guide/cli/drivers/mpas_init/show-schema.out new file mode 100644 index 000000000..998687f90 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/mpas_init/show-schema.out @@ -0,0 +1,21 @@ +{ + "properties": { + "mpas_init": { + "additionalProperties": false, + "properties": { + "boundary_conditions": { + "additionalProperties": false, + "properties": { + "interval_hours": { + "minimum": 1, +... + "streams" + ], + "type": "object" + } + }, + "required": [ + "mpas_init" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/orog_gsl.rst b/docs/sections/user_guide/cli/drivers/orog_gsl.rst index 7f2490b2a..d589651b6 100644 --- a/docs/sections/user_guide/cli/drivers/orog_gsl.rst +++ b/docs/sections/user_guide/cli/drivers/orog_gsl.rst @@ -52,3 +52,11 @@ Its contents are described in section :ref:`orog_gsl_yaml`. $ uw orog_gsl run --config-file config.yaml --batch --dry-run .. include:: /shared/key_path.rst + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: orog_gsl/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: orog_gsl/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/orog_gsl/help.out b/docs/sections/user_guide/cli/drivers/orog_gsl/help.out index 744e588ff..b49285d3d 100644 --- a/docs/sections/user_guide/cli/drivers/orog_gsl/help.out +++ b/docs/sections/user_guide/cli/drivers/orog_gsl/help.out @@ -1,4 +1,4 @@ -usage: uw orog_gsl [-h] [--version] TASK ... +usage: uw orog_gsl [-h] [--version] [--show-schema] TASK ... Execute orog_gsl tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/orog_gsl/show-schema.cmd b/docs/sections/user_guide/cli/drivers/orog_gsl/show-schema.cmd new file mode 100644 index 000000000..3bdf61aac --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/orog_gsl/show-schema.cmd @@ -0,0 +1,2 @@ +uw orog_gsl --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/orog_gsl/show-schema.out b/docs/sections/user_guide/cli/drivers/orog_gsl/show-schema.out new file mode 100644 index 000000000..36a92dfb9 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/orog_gsl/show-schema.out @@ -0,0 +1,21 @@ +{ + "properties": { + "orog_gsl": { + "additionalProperties": false, + "properties": { + "config": { + "additionalProperties": false, + "properties": { + "halo": { + "type": "integer" +... + "rundir" + ], + "type": "object" + } + }, + "required": [ + "orog_gsl" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/sfc_climo_gen.rst b/docs/sections/user_guide/cli/drivers/sfc_climo_gen.rst index 559316077..fe8939667 100644 --- a/docs/sections/user_guide/cli/drivers/sfc_climo_gen.rst +++ b/docs/sections/user_guide/cli/drivers/sfc_climo_gen.rst @@ -58,3 +58,11 @@ Its contents are described in depth in section :ref:`sfc_climo_gen_yaml`. .. code-block:: text $ uw sfc_climo_gen provisioned_rundir --config-file config.yaml --batch + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: sfc_climo_gen/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: sfc_climo_gen/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/sfc_climo_gen/help.out b/docs/sections/user_guide/cli/drivers/sfc_climo_gen/help.out index 28b619718..f6170ae46 100644 --- a/docs/sections/user_guide/cli/drivers/sfc_climo_gen/help.out +++ b/docs/sections/user_guide/cli/drivers/sfc_climo_gen/help.out @@ -1,4 +1,4 @@ -usage: uw sfc_climo_gen [-h] [--version] TASK ... +usage: uw sfc_climo_gen [-h] [--version] [--show-schema] TASK ... Execute sfc_climo_gen tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/sfc_climo_gen/show-schema.cmd b/docs/sections/user_guide/cli/drivers/sfc_climo_gen/show-schema.cmd new file mode 100644 index 000000000..918a4c80b --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/sfc_climo_gen/show-schema.cmd @@ -0,0 +1,2 @@ +uw sfc_climo_gen --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/sfc_climo_gen/show-schema.out b/docs/sections/user_guide/cli/drivers/sfc_climo_gen/show-schema.out new file mode 100644 index 000000000..9cafdced3 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/sfc_climo_gen/show-schema.out @@ -0,0 +1,21 @@ +{ + "properties": { + "sfc_climo_gen": { + "additionalProperties": false, + "properties": { + "execution": { + "additionalProperties": false, + "properties": { + "batchargs": { + "additionalProperties": true, +... + "rundir" + ], + "type": "object" + } + }, + "required": [ + "sfc_climo_gen" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/shave.rst b/docs/sections/user_guide/cli/drivers/shave.rst index d3bef10f2..ff528c5ab 100644 --- a/docs/sections/user_guide/cli/drivers/shave.rst +++ b/docs/sections/user_guide/cli/drivers/shave.rst @@ -52,3 +52,11 @@ Its contents are described in section :ref:`shave_yaml`. $ uw shave run --config-file config.yaml --batch --dry-run .. include:: /shared/key_path.rst + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: shave/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: shave/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/shave/help.out b/docs/sections/user_guide/cli/drivers/shave/help.out index 74056e0aa..7cd91374e 100644 --- a/docs/sections/user_guide/cli/drivers/shave/help.out +++ b/docs/sections/user_guide/cli/drivers/shave/help.out @@ -1,4 +1,4 @@ -usage: uw shave [-h] [--version] TASK ... +usage: uw shave [-h] [--version] [--show-schema] TASK ... Execute shave tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/shave/show-schema.cmd b/docs/sections/user_guide/cli/drivers/shave/show-schema.cmd new file mode 100644 index 000000000..783cabcfe --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/shave/show-schema.cmd @@ -0,0 +1,2 @@ +uw shave --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/shave/show-schema.out b/docs/sections/user_guide/cli/drivers/shave/show-schema.out new file mode 100644 index 000000000..b18e04f5f --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/shave/show-schema.out @@ -0,0 +1,21 @@ +{ + "properties": { + "shave": { + "additionalProperties": false, + "properties": { + "config": { + "additionalProperties": false, + "properties": { + "input_grid_file": { + "type": "string" +... + "rundir" + ], + "type": "object" + } + }, + "required": [ + "shave" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/ungrib.rst b/docs/sections/user_guide/cli/drivers/ungrib.rst index a53a07a63..df25b4e1f 100644 --- a/docs/sections/user_guide/cli/drivers/ungrib.rst +++ b/docs/sections/user_guide/cli/drivers/ungrib.rst @@ -58,3 +58,11 @@ Its contents are described in depth in section :ref:`ungrib_yaml`. .. code-block:: text $ uw ungrib provisioned_rundir --config-file config.yaml --cycle 2021-04-01T12 --batch + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: ungrib/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: ungrib/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/ungrib/help.out b/docs/sections/user_guide/cli/drivers/ungrib/help.out index 8d4fe4816..252272e05 100644 --- a/docs/sections/user_guide/cli/drivers/ungrib/help.out +++ b/docs/sections/user_guide/cli/drivers/ungrib/help.out @@ -1,4 +1,4 @@ -usage: uw ungrib [-h] [--version] TASK ... +usage: uw ungrib [-h] [--version] [--show-schema] TASK ... Execute ungrib tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/ungrib/show-schema.cmd b/docs/sections/user_guide/cli/drivers/ungrib/show-schema.cmd new file mode 100644 index 000000000..04ae66e66 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/ungrib/show-schema.cmd @@ -0,0 +1,2 @@ +uw ungrib --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/ungrib/show-schema.out b/docs/sections/user_guide/cli/drivers/ungrib/show-schema.out new file mode 100644 index 000000000..01684d40b --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/ungrib/show-schema.out @@ -0,0 +1,21 @@ +{ + "properties": { + "ungrib": { + "additionalProperties": false, + "properties": { + "execution": { + "additionalProperties": false, + "properties": { + "batchargs": { + "additionalProperties": true, +... + "vtable" + ], + "type": "object" + } + }, + "required": [ + "ungrib" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/drivers/upp.rst b/docs/sections/user_guide/cli/drivers/upp.rst index 22b06ecb4..ae8d24eb4 100644 --- a/docs/sections/user_guide/cli/drivers/upp.rst +++ b/docs/sections/user_guide/cli/drivers/upp.rst @@ -58,3 +58,11 @@ Its contents are described in depth in section :ref:`upp_yaml`. .. code-block:: text $ uw upp provisioned_rundir --config-file config.yaml --cycle 2024-05-06T12 --leadtime 6 --batch + +* Specifying the ``--show-schema`` flag, with no other options, prints the driver's schema: + +.. literalinclude:: upp/show-schema.cmd + :language: text + :emphasize-lines: 1 +.. literalinclude:: upp/show-schema.out + :language: text diff --git a/docs/sections/user_guide/cli/drivers/upp/help.out b/docs/sections/user_guide/cli/drivers/upp/help.out index 68d5748b7..7163d3f71 100644 --- a/docs/sections/user_guide/cli/drivers/upp/help.out +++ b/docs/sections/user_guide/cli/drivers/upp/help.out @@ -1,4 +1,4 @@ -usage: uw upp [-h] [--version] TASK ... +usage: uw upp [-h] [--version] [--show-schema] TASK ... Execute upp tasks @@ -7,6 +7,8 @@ Optional arguments: Show help and exit --version Show version info and exit + --show-schema + Show driver schema and exit Positional arguments: TASK diff --git a/docs/sections/user_guide/cli/drivers/upp/show-schema.cmd b/docs/sections/user_guide/cli/drivers/upp/show-schema.cmd new file mode 100644 index 000000000..c0a1b290d --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/upp/show-schema.cmd @@ -0,0 +1,2 @@ +uw upp --show-schema >schema +head schema && echo ... && tail schema diff --git a/docs/sections/user_guide/cli/drivers/upp/show-schema.out b/docs/sections/user_guide/cli/drivers/upp/show-schema.out new file mode 100644 index 000000000..11be8c436 --- /dev/null +++ b/docs/sections/user_guide/cli/drivers/upp/show-schema.out @@ -0,0 +1,21 @@ +{ + "properties": { + "upp": { + "additionalProperties": false, + "properties": { + "execution": { + "additionalProperties": false, + "properties": { + "batchargs": { + "additionalProperties": true, +... + "rundir" + ], + "type": "object" + } + }, + "required": [ + "upp" + ], + "type": "object" +} diff --git a/docs/sections/user_guide/cli/tools/execute/rand.py b/docs/sections/user_guide/cli/tools/execute/rand.py index 2a8e10b57..99e5853c9 100644 --- a/docs/sections/user_guide/cli/tools/execute/rand.py +++ b/docs/sections/user_guide/cli/tools/execute/rand.py @@ -23,6 +23,6 @@ def randfile(self): with open(path, "w", encoding="utf-8") as f: print(randint(self.config["lo"], self.config["hi"]), file=f) - @property - def driver_name(self): + @classmethod + def driver_name(cls): return "rand" diff --git a/src/uwtools/api/cdeps.py b/src/uwtools/api/cdeps.py index 0c91a6b06..f6e112309 100644 --- a/src/uwtools/api/cdeps.py +++ b/src/uwtools/api/cdeps.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``cdeps`` driver. """ -from functools import partial - from uwtools.drivers.cdeps import CDEPS -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = CDEPS execute = _make_execute(_driver, with_cycle=True) -tasks = partial(tasks, _driver) -__all__ = ["CDEPS", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["CDEPS", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/chgres_cube.py b/src/uwtools/api/chgres_cube.py index 51fd53f90..477f892e7 100644 --- a/src/uwtools/api/chgres_cube.py +++ b/src/uwtools/api/chgres_cube.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``chgres_cube`` driver. """ -from functools import partial - from uwtools.drivers.chgres_cube import ChgresCube -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = ChgresCube execute = _make_execute(_driver, with_cycle=True) -tasks = partial(tasks, _driver) -__all__ = ["ChgresCube", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["ChgresCube", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/esg_grid.py b/src/uwtools/api/esg_grid.py index 06e3bac41..9bbd7e6f5 100644 --- a/src/uwtools/api/esg_grid.py +++ b/src/uwtools/api/esg_grid.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``esg_grid`` driver. """ -from functools import partial - from uwtools.drivers.esg_grid import ESGGrid -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = ESGGrid execute = _make_execute(_driver) -tasks = partial(tasks, _driver) -__all__ = ["ESGGrid", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["ESGGrid", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/filter_topo.py b/src/uwtools/api/filter_topo.py index fac93ecf6..ded7d6cd6 100644 --- a/src/uwtools/api/filter_topo.py +++ b/src/uwtools/api/filter_topo.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``filter_topo`` driver. """ -from functools import partial - from uwtools.drivers.filter_topo import FilterTopo -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = FilterTopo execute = _make_execute(_driver) -tasks = partial(tasks, _driver) -__all__ = ["FilterTopo", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["FilterTopo", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/fv3.py b/src/uwtools/api/fv3.py index 4c613b2dd..b5a9401b0 100644 --- a/src/uwtools/api/fv3.py +++ b/src/uwtools/api/fv3.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``fv3`` driver. """ -from functools import partial - from uwtools.drivers.fv3 import FV3 -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = FV3 execute = _make_execute(_driver, with_cycle=True) -tasks = partial(tasks, _driver) -__all__ = ["FV3", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["FV3", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/global_equiv_resol.py b/src/uwtools/api/global_equiv_resol.py index 6bd3940b5..6a6484354 100644 --- a/src/uwtools/api/global_equiv_resol.py +++ b/src/uwtools/api/global_equiv_resol.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``global_equiv_resol`` driver. """ -from functools import partial - from uwtools.drivers.global_equiv_resol import GlobalEquivResol -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = GlobalEquivResol execute = _make_execute(_driver) -tasks = partial(tasks, _driver) -__all__ = ["GlobalEquivResol", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["GlobalEquivResol", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/ioda.py b/src/uwtools/api/ioda.py index 5abe332ca..cea6e5fa1 100644 --- a/src/uwtools/api/ioda.py +++ b/src/uwtools/api/ioda.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``ioda`` driver. """ -from functools import partial - from uwtools.drivers.ioda import IODA -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = IODA execute = _make_execute(_driver, with_cycle=True) -tasks = partial(tasks, _driver) -__all__ = ["IODA", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["IODA", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/jedi.py b/src/uwtools/api/jedi.py index c0986c277..1cb0f1ff1 100644 --- a/src/uwtools/api/jedi.py +++ b/src/uwtools/api/jedi.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``jedi`` driver. """ -from functools import partial - from uwtools.drivers.jedi import JEDI -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = JEDI execute = _make_execute(_driver, with_cycle=True) -tasks = partial(tasks, _driver) -__all__ = ["JEDI", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["JEDI", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/make_hgrid.py b/src/uwtools/api/make_hgrid.py index 63886ce03..68de11ff4 100644 --- a/src/uwtools/api/make_hgrid.py +++ b/src/uwtools/api/make_hgrid.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``make_hgrid`` driver. """ -from functools import partial - from uwtools.drivers.make_hgrid import MakeHgrid -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = MakeHgrid execute = _make_execute(_driver) -tasks = partial(tasks, _driver) -__all__ = ["MakeHgrid", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["MakeHgrid", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/make_solo_mosaic.py b/src/uwtools/api/make_solo_mosaic.py index d9dc4ebe7..c6ddb6bc9 100644 --- a/src/uwtools/api/make_solo_mosaic.py +++ b/src/uwtools/api/make_solo_mosaic.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``make_solo_mosaic`` driver. """ -from functools import partial - from uwtools.drivers.make_solo_mosaic import MakeSoloMosaic -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = MakeSoloMosaic execute = _make_execute(_driver) -tasks = partial(tasks, _driver) -__all__ = ["MakeSoloMosaic", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["MakeSoloMosaic", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/mpas.py b/src/uwtools/api/mpas.py index 21ccb9cdb..3ce1245ad 100644 --- a/src/uwtools/api/mpas.py +++ b/src/uwtools/api/mpas.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``mpas`` driver. """ -from functools import partial - from uwtools.drivers.mpas import MPAS -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = MPAS execute = _make_execute(_driver, with_cycle=True) -tasks = partial(tasks, _driver) -__all__ = ["MPAS", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["MPAS", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/mpas_init.py b/src/uwtools/api/mpas_init.py index 2654af782..2308a578f 100644 --- a/src/uwtools/api/mpas_init.py +++ b/src/uwtools/api/mpas_init.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``mpas_init`` driver. """ -from functools import partial - from uwtools.drivers.mpas_init import MPASInit -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = MPASInit execute = _make_execute(_driver, with_cycle=True) -tasks = partial(tasks, _driver) -__all__ = ["MPASInit", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["MPASInit", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/orog_gsl.py b/src/uwtools/api/orog_gsl.py index ff6973866..f56c7f67e 100644 --- a/src/uwtools/api/orog_gsl.py +++ b/src/uwtools/api/orog_gsl.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``orog_gsl`` driver. """ -from functools import partial - from uwtools.drivers.orog_gsl import OrogGSL -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = OrogGSL execute = _make_execute(_driver) -tasks = partial(tasks, _driver) -__all__ = ["OrogGSL", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["OrogGSL", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/schism.py b/src/uwtools/api/schism.py index 50cfe73f5..50e4e3ffb 100644 --- a/src/uwtools/api/schism.py +++ b/src/uwtools/api/schism.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``schism`` driver. """ -from functools import partial - from uwtools.drivers.schism import SCHISM -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = SCHISM execute = _make_execute(_driver, with_cycle=True) -tasks = partial(tasks, _driver) -__all__ = ["SCHISM", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["SCHISM", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/sfc_climo_gen.py b/src/uwtools/api/sfc_climo_gen.py index 7e881def3..d882d6357 100644 --- a/src/uwtools/api/sfc_climo_gen.py +++ b/src/uwtools/api/sfc_climo_gen.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``sfc_climo_gen`` driver. """ -from functools import partial - from uwtools.drivers.sfc_climo_gen import SfcClimoGen -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = SfcClimoGen execute = _make_execute(_driver) -tasks = partial(tasks, _driver) -__all__ = ["SfcClimoGen", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["SfcClimoGen", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/shave.py b/src/uwtools/api/shave.py index ff3b9911f..0dd01ffd8 100644 --- a/src/uwtools/api/shave.py +++ b/src/uwtools/api/shave.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``shave`` driver. """ -from functools import partial - from uwtools.drivers.shave import Shave -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.utils.api import make_execute as _make_execute _driver = Shave execute = _make_execute(_driver) -tasks = partial(tasks, _driver) -__all__ = ["Shave", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["Shave", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/ungrib.py b/src/uwtools/api/ungrib.py index 1bfa0c7fa..34a353dbd 100644 --- a/src/uwtools/api/ungrib.py +++ b/src/uwtools/api/ungrib.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``ungrib`` driver. """ -from functools import partial - -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.drivers.ungrib import Ungrib from uwtools.utils.api import make_execute as _make_execute _driver = Ungrib execute = _make_execute(_driver, with_cycle=True) -tasks = partial(tasks, _driver) -__all__ = ["Ungrib", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["Ungrib", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/upp.py b/src/uwtools/api/upp.py index 9a098df42..0def005f9 100644 --- a/src/uwtools/api/upp.py +++ b/src/uwtools/api/upp.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``upp`` driver. """ -from functools import partial - -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.drivers.upp import UPP from uwtools.utils.api import make_execute as _make_execute _driver = UPP execute = _make_execute(_driver, with_cycle=True, with_leadtime=True) -tasks = partial(tasks, _driver) -__all__ = ["UPP", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["UPP", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/api/ww3.py b/src/uwtools/api/ww3.py index dd1a4759f..25fe70e32 100644 --- a/src/uwtools/api/ww3.py +++ b/src/uwtools/api/ww3.py @@ -2,13 +2,27 @@ API access to the ``uwtools`` ``ww3`` driver. """ -from functools import partial - -from uwtools.drivers.support import graph, tasks +from uwtools.drivers.support import graph +from uwtools.drivers.support import tasks as _tasks from uwtools.drivers.ww3 import WaveWatchIII from uwtools.utils.api import make_execute as _make_execute _driver = WaveWatchIII execute = _make_execute(_driver, with_cycle=True) -tasks = partial(tasks, _driver) -__all__ = ["WaveWatchIII", "execute", "graph", "tasks"] + + +def schema() -> dict: + """ + Return the driver's schema. + """ + return _driver.schema() + + +def tasks() -> dict[str, str]: + """ + Return a mapping from task names to their one-line descriptions. + """ + return _tasks(_driver) + + +__all__ = ["WaveWatchIII", "execute", "graph", "schema", "tasks"] diff --git a/src/uwtools/cli.py b/src/uwtools/cli.py index a96e2d5df..15f3dda36 100644 --- a/src/uwtools/cli.py +++ b/src/uwtools/cli.py @@ -52,9 +52,9 @@ def main() -> None: setup_logging(quiet=True) args, checks = _parse_args(sys.argv[1:]) args[STR.action] = args.get(STR.action, args[STR.mode]) - for check in checks[args[STR.mode]][args[STR.action]]: + for check in checks[args[STR.mode]].get(args[STR.action], []): check(args) - setup_logging(quiet=args[STR.quiet], verbose=args[STR.verbose]) + setup_logging(quiet=args.get(STR.quiet, False), verbose=args.get(STR.verbose, False)) except UWError as e: _abort(str(e)) try: @@ -830,6 +830,14 @@ def _add_arg_search_path(group: Group) -> None: ) +def _add_arg_show_schema(group: Group) -> None: + group.add_argument( + _switch(STR.showschema), + action="store_true", + help="Show driver schema and exit", + ) + + def _add_arg_target_dir( group: Group, required: bool = False, helpmsg: Optional[str] = None ) -> None: @@ -977,8 +985,9 @@ def _add_subparser_for_driver( :param with_leadtime: Does this driver require a leadtime? """ parser = _add_subparser(subparsers, name, "Execute %s tasks" % name) - _basic_setup(parser) - subparsers = _add_subparsers(parser, STR.action, STR.task.upper()) + optional = _basic_setup(parser) + _add_arg_show_schema(optional) + subparsers = _add_subparsers(parser, STR.action, STR.task.upper(), required=False) return { task: _add_subparser_for_driver_task( subparsers, task, helpmsg, with_batch, with_cycle, with_leadtime @@ -1026,7 +1035,7 @@ def _add_subparser_for_driver_task( return checks -def _add_subparsers(parser: Parser, dest: str, metavar: str) -> Subparsers: +def _add_subparsers(parser: Parser, dest: str, metavar: str, required: bool = True) -> Subparsers: """ Add subparsers to a parser. @@ -1036,7 +1045,7 @@ def _add_subparsers(parser: Parser, dest: str, metavar: str) -> Subparsers: :return: The new subparsers object. """ return parser.add_subparsers( - dest=dest, metavar=metavar, required=True, title="Positional arguments" + dest=dest, metavar=metavar, required=required, title="Positional arguments" ) @@ -1106,7 +1115,13 @@ def _dispatch_to_driver(name: str, args: Args) -> bool: :param name: Name of the driver to dispatch to. :param args: Parsed command-line args. """ - execute: Callable[..., bool] = import_module("uwtools.api.%s" % name).execute + module = import_module("uwtools.api.%s" % name) + if args.get(STR.showschema): + print(json.dumps(module.schema(), sort_keys=True, indent=2)) + return True + if not args.get(STR.action): + _abort("No %s specified" % STR.task.upper()) + execute: Callable[..., bool] = module.execute kwargs = { "task": args[STR.action], "config": args[STR.cfgfile], diff --git a/src/uwtools/config/formats/yaml.py b/src/uwtools/config/formats/yaml.py index a86f7a674..c23ef6200 100644 --- a/src/uwtools/config/formats/yaml.py +++ b/src/uwtools/config/formats/yaml.py @@ -100,9 +100,10 @@ def _load(self, config_file: Optional[Path]) -> dict: config = yaml.load(f.read(), Loader=loader) if isinstance(config, dict): return config + t = type(config).__name__ raise UWConfigError( - "Parsed a %s value from %s, expected a dict" - % (type(config).__name__, config_file or "stdin") + "Parsed a%s %s value from %s, expected a dict" + % ("n" if t[0] in "aeiou" else "", t, config_file or "stdin") ) except yaml.constructor.ConstructorError as e: if e.problem: diff --git a/src/uwtools/config/validator.py b/src/uwtools/config/validator.py index 7a30696ec..42b723ed6 100644 --- a/src/uwtools/config/validator.py +++ b/src/uwtools/config/validator.py @@ -3,6 +3,7 @@ """ import json +from functools import cache from pathlib import Path from typing import Optional, Union @@ -19,6 +20,27 @@ # Public functions +def bundle(schema: dict) -> dict: + """ + Bundle a schema by dereferencing links to other schemas. + + :param schema: A JSON Schema. + :returns: The bundled schema. + """ + key = "$ref" + bundled = {} + for k, v in schema.items(): + if isinstance(v, dict): + if list(v.keys()) == [key] and v[key].startswith("urn:uwtools:"): + # i.e. the current key's value is of the form: {"$ref": "urn:uwtools:.*"} + bundled[k] = bundle(_registry().get_or_retrieve(v[key]).value.contents) + else: + bundled[k] = bundle(v) + else: + bundled[k] = v + return bundled + + def get_schema_file(schema_name: str) -> Path: """ Return the path to the JSON Schema file for a given name. @@ -95,13 +117,10 @@ def _prep_config(config: Union[dict, YAMLConfig, Optional[Path]]) -> YAMLConfig: return cfgobj -def _validation_errors(config: Union[dict, list], schema: dict) -> list[ValidationError]: +@cache +def _registry() -> Registry: """ - Identify schema-validation errors. - - :param config: A config to validate. - :param schema: JSON Schema to validate the config against. - :return: Any validation errors. + Return a JSON Schema registry resolving urn:uwtools:* references. """ # See https://github.com/python-jsonschema/referencing/issues/61 about typing issues. @@ -111,6 +130,16 @@ def retrieve(uri: str) -> Resource: with open(resource_path(f"jsonschema/{name}.jsonschema"), "r", encoding="utf-8") as f: return Resource(contents=json.load(f), specification=DRAFT202012) # type: ignore - registry = Registry(retrieve=retrieve) # type: ignore - validator = Draft202012Validator(schema, registry=registry) + return Registry(retrieve=retrieve) # type: ignore + + +def _validation_errors(config: Union[dict, list], schema: dict) -> list[ValidationError]: + """ + Identify schema-validation errors. + + :param config: A config to validate. + :param schema: JSON Schema to validate the config against. + :return: Any validation errors. + """ + validator = Draft202012Validator(schema, registry=_registry()) return list(validator.iter_errors(config)) diff --git a/src/uwtools/drivers/cdeps.py b/src/uwtools/drivers/cdeps.py index 869512df5..a3a481797 100644 --- a/src/uwtools/drivers/cdeps.py +++ b/src/uwtools/drivers/cdeps.py @@ -95,8 +95,8 @@ def ocn_stream(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/chgres_cube.py b/src/uwtools/drivers/chgres_cube.py index 94536decf..669860b07 100644 --- a/src/uwtools/drivers/chgres_cube.py +++ b/src/uwtools/drivers/chgres_cube.py @@ -91,8 +91,8 @@ def runscript(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/driver.py b/src/uwtools/drivers/driver.py index 40c211987..1a60a6d14 100644 --- a/src/uwtools/drivers/driver.py +++ b/src/uwtools/drivers/driver.py @@ -19,7 +19,13 @@ from uwtools.config.formats.base import Config from uwtools.config.formats.yaml import YAMLConfig from uwtools.config.tools import walk_key_path -from uwtools.config.validator import get_schema_file, validate, validate_external, validate_internal +from uwtools.config.validator import ( + bundle, + get_schema_file, + validate, + validate_external, + validate_internal, +) from uwtools.exceptions import UWConfigError from uwtools.logging import log from uwtools.scheduler import JobScheduler @@ -56,9 +62,9 @@ def __init__( self._config_full: dict = config_input.data self._config_intermediate, _ = walk_key_path(self._config_full, key_path or []) try: - self._config: dict = self._config_intermediate[self.driver_name] + self._config: dict = self._config_intermediate[self.driver_name()] except KeyError as e: - raise UWConfigError("Required '%s' block missing in config" % self.driver_name) from e + raise UWConfigError("Required '%s' block missing in config" % self.driver_name()) from e if controller: self._config[STR.rundir] = self._config_intermediate[controller][STR.rundir] self._validate(schema_file) @@ -74,7 +80,7 @@ def __repr__(self) -> str: return " ".join(filter(None, [str(self), cycle, leadtime, "in", self.config[STR.rundir]])) def __str__(self) -> str: - return self.driver_name + return self.driver_name() @property def config(self) -> dict: @@ -97,6 +103,14 @@ def rundir(self) -> Path: """ return Path(self.config[STR.rundir]).absolute() + @classmethod + def schema(cls) -> dict: + """ + Return the driver's schema. + """ + with open(get_schema_file(schema_name=cls._schema_name()), "r", encoding="utf-8") as f: + return bundle(json.load(f)) + def taskname(self, suffix: str) -> str: """ Return a common tag for graph-task log messages. @@ -110,7 +124,7 @@ def taskname(self, suffix: str) -> str: if cycle and leadtime is not None else cycle.strftime("%Y%m%d %HZ") if cycle else None ) - return " ".join(filter(None, [timestr, self.driver_name, suffix])) + return " ".join(filter(None, [timestr, self.driver_name(), suffix])) # Workflow tasks @@ -154,9 +168,9 @@ def _create_user_updated_config( # Public helper methods - @property + @classmethod @abstractmethod - def driver_name(self) -> str: + def driver_name(cls) -> str: """ The name of this driver. """ @@ -177,12 +191,12 @@ def _namelist_schema( for config_key in config_keys or [STR.namelist]: nmlcfg = nmlcfg[config_key] if nmlcfg.get(STR.validate, True): - schema_file = get_schema_file(schema_name=self.driver_name.replace("_", "-")) + schema_file = get_schema_file(schema_name=self._schema_name()) with open(schema_file, "r", encoding="utf-8") as f: schema = json.load(f) for schema_key in schema_keys or [ STR.properties, - self.driver_name, + self.driver_name(), STR.properties, STR.namelist, STR.properties, @@ -191,6 +205,13 @@ def _namelist_schema( schema = schema[schema_key] return schema + @classmethod + def _schema_name(cls) -> str: + """ + Return the filename stem for this driver's schema file. + """ + return cls.driver_name().replace("_", "-") + def _validate(self, schema_file: Optional[Path] = None) -> None: """ Perform all necessary schema validation. @@ -201,9 +222,7 @@ def _validate(self, schema_file: Optional[Path] = None) -> None: if schema_file: validate_external(schema_file=schema_file, config=self._config_intermediate) else: - validate_internal( - schema_name=self.driver_name.replace("_", "-"), config=self._config_intermediate - ) + validate_internal(schema_name=self._schema_name(), config=self._config_intermediate) class AssetsCycleBased(Assets): @@ -463,7 +482,7 @@ def _runscript_path(self) -> Path: """ The path to the runscript. """ - return self.rundir / f"runscript.{self.driver_name}" + return self.rundir / f"runscript.{self.driver_name()}" @property def _scheduler(self) -> JobScheduler: diff --git a/src/uwtools/drivers/esg_grid.py b/src/uwtools/drivers/esg_grid.py index 1b51ad120..0d77a7da8 100644 --- a/src/uwtools/drivers/esg_grid.py +++ b/src/uwtools/drivers/esg_grid.py @@ -51,8 +51,8 @@ def provisioned_rundir(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/filter_topo.py b/src/uwtools/drivers/filter_topo.py index 500dfb766..3cb396a5d 100644 --- a/src/uwtools/drivers/filter_topo.py +++ b/src/uwtools/drivers/filter_topo.py @@ -62,8 +62,8 @@ def provisioned_rundir(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/fv3.py b/src/uwtools/drivers/fv3.py index de5da8607..e417f58d1 100644 --- a/src/uwtools/drivers/fv3.py +++ b/src/uwtools/drivers/fv3.py @@ -178,8 +178,8 @@ def runscript(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/global_equiv_resol.py b/src/uwtools/drivers/global_equiv_resol.py index 1accc4008..25b3ef8a6 100644 --- a/src/uwtools/drivers/global_equiv_resol.py +++ b/src/uwtools/drivers/global_equiv_resol.py @@ -40,8 +40,8 @@ def provisioned_rundir(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/ioda.py b/src/uwtools/drivers/ioda.py index 38561deba..533b7faa6 100644 --- a/src/uwtools/drivers/ioda.py +++ b/src/uwtools/drivers/ioda.py @@ -31,8 +31,8 @@ def provisioned_rundir(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/jedi.py b/src/uwtools/drivers/jedi.py index ae5de12f3..a2467cdef 100644 --- a/src/uwtools/drivers/jedi.py +++ b/src/uwtools/drivers/jedi.py @@ -56,8 +56,8 @@ def validate_only(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/make_hgrid.py b/src/uwtools/drivers/make_hgrid.py index 1cc3f9ba9..28f275953 100644 --- a/src/uwtools/drivers/make_hgrid.py +++ b/src/uwtools/drivers/make_hgrid.py @@ -26,8 +26,8 @@ def provisioned_rundir(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/make_solo_mosaic.py b/src/uwtools/drivers/make_solo_mosaic.py index 8da9afe00..d9598edac 100644 --- a/src/uwtools/drivers/make_solo_mosaic.py +++ b/src/uwtools/drivers/make_solo_mosaic.py @@ -32,12 +32,12 @@ def taskname(self, suffix: str) -> str: :param suffix: Log-string suffix. """ - return "%s %s" % (self.driver_name, suffix) + return "%s %s" % (self.driver_name(), suffix) # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/mpas.py b/src/uwtools/drivers/mpas.py index 800e256ad..c1953f822 100644 --- a/src/uwtools/drivers/mpas.py +++ b/src/uwtools/drivers/mpas.py @@ -68,8 +68,8 @@ def namelist_file(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/mpas_init.py b/src/uwtools/drivers/mpas_init.py index 29ffb3dd4..47d954fbd 100644 --- a/src/uwtools/drivers/mpas_init.py +++ b/src/uwtools/drivers/mpas_init.py @@ -70,8 +70,8 @@ def namelist_file(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/orog_gsl.py b/src/uwtools/drivers/orog_gsl.py index 470fc77a9..bfde683e7 100644 --- a/src/uwtools/drivers/orog_gsl.py +++ b/src/uwtools/drivers/orog_gsl.py @@ -72,8 +72,8 @@ def topo_data_30s(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/schism.py b/src/uwtools/drivers/schism.py index 98ea5d13a..1f6fa30ef 100644 --- a/src/uwtools/drivers/schism.py +++ b/src/uwtools/drivers/schism.py @@ -47,8 +47,8 @@ def provisioned_rundir(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/sfc_climo_gen.py b/src/uwtools/drivers/sfc_climo_gen.py index 1d662237b..035ccc07f 100644 --- a/src/uwtools/drivers/sfc_climo_gen.py +++ b/src/uwtools/drivers/sfc_climo_gen.py @@ -54,8 +54,8 @@ def provisioned_rundir(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/shave.py b/src/uwtools/drivers/shave.py index b3c9c4c61..868d38982 100644 --- a/src/uwtools/drivers/shave.py +++ b/src/uwtools/drivers/shave.py @@ -26,8 +26,8 @@ def provisioned_rundir(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/ungrib.py b/src/uwtools/drivers/ungrib.py index 0c2c20f9f..22715fd22 100644 --- a/src/uwtools/drivers/ungrib.py +++ b/src/uwtools/drivers/ungrib.py @@ -104,8 +104,8 @@ def vtable(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/upp.py b/src/uwtools/drivers/upp.py index dfbed309f..eed851355 100644 --- a/src/uwtools/drivers/upp.py +++ b/src/uwtools/drivers/upp.py @@ -74,8 +74,8 @@ def provisioned_rundir(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/drivers/ww3.py b/src/uwtools/drivers/ww3.py index 0cb451a1d..a177eb438 100644 --- a/src/uwtools/drivers/ww3.py +++ b/src/uwtools/drivers/ww3.py @@ -61,8 +61,8 @@ def restart_directory(self): # Public helper methods - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: """ The name of this driver. """ diff --git a/src/uwtools/strings.py b/src/uwtools/strings.py index 45d400bce..098724d62 100644 --- a/src/uwtools/strings.py +++ b/src/uwtools/strings.py @@ -128,6 +128,7 @@ class STR: searchpath: str = "search_path" sfcclimogen: str = "sfc_climo_gen" shave: str = "shave" + showschema: str = "show_schema" stdout: str = "stdout" targetdir: str = "target_dir" task: str = "task" diff --git a/src/uwtools/tests/api/test_drivers.py b/src/uwtools/tests/api/test_drivers.py index 3b957f7ad..70bf280b3 100644 --- a/src/uwtools/tests/api/test_drivers.py +++ b/src/uwtools/tests/api/test_drivers.py @@ -10,8 +10,10 @@ cdeps, chgres_cube, esg_grid, + filter_topo, fv3, global_equiv_resol, + ioda, jedi, make_hgrid, make_solo_mosaic, @@ -32,8 +34,10 @@ cdeps, chgres_cube, esg_grid, + filter_topo, fv3, global_equiv_resol, + ioda, jedi, make_hgrid, make_solo_mosaic, @@ -47,7 +51,7 @@ upp, ww3, ] -with_cycle = [cdeps, chgres_cube, fv3, jedi, mpas, mpas_init, schism, ungrib, upp, ww3] +with_cycle = [cdeps, chgres_cube, fv3, ioda, jedi, mpas, mpas_init, schism, ungrib, upp, ww3] with_leadtime = [upp] @@ -82,6 +86,11 @@ def test_api_graph(module): assert module.graph is support.graph +@mark.parametrize("module", modules) +def test_api_schema(module): + assert module.schema() + + @mark.parametrize("module", modules) def test_api_tasks(module): with patch.object(iotaa, "tasknames") as tasknames: diff --git a/src/uwtools/tests/config/formats/test_yaml.py b/src/uwtools/tests/config/formats/test_yaml.py index 7a5e025b0..98a374d59 100644 --- a/src/uwtools/tests/config/formats/test_yaml.py +++ b/src/uwtools/tests/config/formats/test_yaml.py @@ -155,10 +155,10 @@ def test_yaml_constructor_error_not_dict_from_file(tmp_path): def test_yaml_constructor_error_not_dict_from_stdin(): # Test that a useful exception is raised if the YAML stdin input is a non-dict value. - with StringIO("a string") as sio, patch.object(sys, "stdin", new=sio): + with StringIO("88") as sio, patch.object(sys, "stdin", new=sio): with raises(exceptions.UWConfigError) as e: YAMLConfig() - assert "Parsed a str value from stdin, expected a dict" in str(e.value) + assert "Parsed an int value from stdin, expected a dict" in str(e.value) def test_yaml_constructor_error_unregistered_constructor(tmp_path): diff --git a/src/uwtools/tests/config/test_validator.py b/src/uwtools/tests/config/test_validator.py index 098cfe284..983c7abf0 100644 --- a/src/uwtools/tests/config/test_validator.py +++ b/src/uwtools/tests/config/test_validator.py @@ -6,7 +6,7 @@ import logging from pathlib import Path from typing import Any -from unittest.mock import patch +from unittest.mock import Mock, patch import yaml from pytest import fixture, raises @@ -124,6 +124,21 @@ def write_as_json(data: dict[str, Any], path: Path) -> Path: # Test functions +def test_bundle(): + schema = {"fruit": {"$ref": "urn:uwtools:a"}, "flowers": None} + with patch.object(validator, "_registry") as _registry: + outer, inner = Mock(), Mock() + outer.value.contents = {"a": {"$ref": "urn:uwtools:attrs"}, "b": {"name": "banana"}} + inner.value.contents = {"name": "apple"} + _registry().get_or_retrieve.side_effect = [outer, inner] + bundled = validator.bundle(schema) + assert bundled == {"fruit": {"a": {"name": "apple"}, "b": {"name": "banana"}}, "flowers": None} + assert [_registry().get_or_retrieve.mock_calls[i].args[0] for i in (0, 1)] == [ + "urn:uwtools:a", + "urn:uwtools:attrs", + ] + + def test_get_schema_file(): with patch.object(validator, "resource_path", return_value=Path("/foo/bar")): assert validator.get_schema_file("baz") == Path("/foo/bar/baz.jsonschema") @@ -191,6 +206,18 @@ def test__prep_config_file(prep_config_dict, tmp_path): assert cfgobj == {"roses": "red", "color": "red"} +def test__registry(tmp_path): + validator._registry.cache_clear() + d = {"foo": "bar"} + path = tmp_path / "foo-bar.jsonschema" + with open(path, "w", encoding="utf-8") as f: + json.dump(d, f) + with patch.object(validator, "resource_path", return_value=path) as resource_path: + r = validator._registry() + assert r.get_or_retrieve("urn:uwtools:foo-bar").value.contents == d + resource_path.assert_called_once_with("jsonschema/foo-bar.jsonschema") + + def test__validation_errors_bad_enum_value(config, schema): config["color"] = "yellow" assert len(validator._validation_errors(config, schema)) == 1 diff --git a/src/uwtools/tests/drivers/test_cdeps.py b/src/uwtools/tests/drivers/test_cdeps.py index e4ecfd5a6..66ad90867 100644 --- a/src/uwtools/tests/drivers/test_cdeps.py +++ b/src/uwtools/tests/drivers/test_cdeps.py @@ -112,7 +112,7 @@ def test_CDEPS_streams(driverobj, group): def test_CDEPS_driver_name(driverobj): - assert driverobj.driver_name == "cdeps" + assert driverobj.driver_name() == CDEPS.driver_name() == "cdeps" def test_CDEPS__model_namelist_file(driverobj): diff --git a/src/uwtools/tests/drivers/test_chgres_cube.py b/src/uwtools/tests/drivers/test_chgres_cube.py index 66ae79d4f..de399e7fe 100644 --- a/src/uwtools/tests/drivers/test_chgres_cube.py +++ b/src/uwtools/tests/drivers/test_chgres_cube.py @@ -148,7 +148,7 @@ def test_ChgresCube_runscript(driverobj): def test_ChgresCube_driver_name(driverobj): - assert driverobj.driver_name == "chgres_cube" + assert driverobj.driver_name() == ChgresCube.driver_name() == "chgres_cube" def test_ChgresCube_taskname(driverobj): diff --git a/src/uwtools/tests/drivers/test_driver.py b/src/uwtools/tests/drivers/test_driver.py index 4bab94f67..9c80c7769 100644 --- a/src/uwtools/tests/drivers/test_driver.py +++ b/src/uwtools/tests/drivers/test_driver.py @@ -39,8 +39,8 @@ def atask(self): def provisioned_rundir(self): pass - @property - def driver_name(self) -> str: + @classmethod + def driver_name(cls) -> str: return "concrete" def _validate(self, schema_file: Optional[Path] = None) -> None: @@ -218,7 +218,7 @@ def test_Assets_key_path(config, tmp_path): assetsobj = ConcreteAssetsTimeInvariant( config=config_file, dry_run=False, key_path=["foo", "bar"] ) - assert assetsobj.config == config[assetsobj.driver_name] + assert assetsobj.config == config[assetsobj.driver_name()] def test_Assets_leadtime(config): @@ -273,6 +273,11 @@ def test_Assets__rundir(assetsobj): assert assetsobj.rundir == Path(assetsobj.config["rundir"]) +def test_Assets__schema_name(): + with patch.object(driver.Assets, "driver_name", return_value="a_driver"): + assert driver.Assets._schema_name() == "a-driver" + + def test_Assets__validate_internal(assetsobj): with patch.object(assetsobj, "_validate", driver.Assets._validate): with patch.object(driver, "validate_internal") as validate_internal: diff --git a/src/uwtools/tests/drivers/test_esg_grid.py b/src/uwtools/tests/drivers/test_esg_grid.py index 838c480a5..950ce2d6d 100644 --- a/src/uwtools/tests/drivers/test_esg_grid.py +++ b/src/uwtools/tests/drivers/test_esg_grid.py @@ -124,4 +124,4 @@ def test_ESGGrid_provisioned_rundir(driverobj): def test_FilterTopo_driver_name(driverobj): - assert driverobj.driver_name == "esg_grid" + assert driverobj.driver_name() == ESGGrid.driver_name() == "esg_grid" diff --git a/src/uwtools/tests/drivers/test_filter_topo.py b/src/uwtools/tests/drivers/test_filter_topo.py index e02bb24c3..54b243fdf 100644 --- a/src/uwtools/tests/drivers/test_filter_topo.py +++ b/src/uwtools/tests/drivers/test_filter_topo.py @@ -101,4 +101,4 @@ def test_FilterTopo_provisioned_rundir(driverobj): def test_FilterTopo_driver_name(driverobj): - assert driverobj.driver_name == "filter_topo" + assert driverobj.driver_name() == FilterTopo.driver_name() == "filter_topo" diff --git a/src/uwtools/tests/drivers/test_fv3.py b/src/uwtools/tests/drivers/test_fv3.py index c5997c768..ebc2a08bf 100644 --- a/src/uwtools/tests/drivers/test_fv3.py +++ b/src/uwtools/tests/drivers/test_fv3.py @@ -248,7 +248,7 @@ def test_FV3_runscript(driverobj): def test_FV3_driver_name(driverobj): - assert driverobj.driver_name == "fv3" + assert driverobj.driver_name() == FV3.driver_name() == "fv3" def test_FV3_taskname(driverobj): diff --git a/src/uwtools/tests/drivers/test_global_equiv_resol.py b/src/uwtools/tests/drivers/test_global_equiv_resol.py index 678b84bf7..f95201940 100644 --- a/src/uwtools/tests/drivers/test_global_equiv_resol.py +++ b/src/uwtools/tests/drivers/test_global_equiv_resol.py @@ -84,7 +84,7 @@ def test_GlobalEquivResol_provisioned_rundir(driverobj): def test_FilterTopo_driver_name(driverobj): - assert driverobj.driver_name == "global_equiv_resol" + assert driverobj.driver_name() == GlobalEquivResol.driver_name() == "global_equiv_resol" def test_GlobalEquivResol__runcmd(driverobj): diff --git a/src/uwtools/tests/drivers/test_ioda.py b/src/uwtools/tests/drivers/test_ioda.py index 2c0442bba..65527f86c 100644 --- a/src/uwtools/tests/drivers/test_ioda.py +++ b/src/uwtools/tests/drivers/test_ioda.py @@ -101,7 +101,7 @@ def test_IODA_provisioned_rundir(driverobj): def test_IODA_driver_name(driverobj): - assert driverobj.driver_name == "ioda" + assert driverobj.driver_name() == IODA.driver_name() == "ioda" def test_IODA__config_fn(driverobj): diff --git a/src/uwtools/tests/drivers/test_jedi.py b/src/uwtools/tests/drivers/test_jedi.py index 5e3ea8e6e..ebd7ecfcc 100644 --- a/src/uwtools/tests/drivers/test_jedi.py +++ b/src/uwtools/tests/drivers/test_jedi.py @@ -189,7 +189,7 @@ def file(path: Path): def test_JEDI_driver_name(driverobj): - assert driverobj.driver_name == "jedi" + assert driverobj.driver_name() == JEDI.driver_name() == "jedi" def test_JEDI__config_fn(driverobj): diff --git a/src/uwtools/tests/drivers/test_make_hgrid.py b/src/uwtools/tests/drivers/test_make_hgrid.py index 46a24d02e..74874e40d 100644 --- a/src/uwtools/tests/drivers/test_make_hgrid.py +++ b/src/uwtools/tests/drivers/test_make_hgrid.py @@ -77,7 +77,7 @@ def test_MakeHgrid_provisioned_rundir(driverobj): def test_MakeHgrid_driver_name(driverobj): - assert driverobj.driver_name == "make_hgrid" + assert driverobj.driver_name() == MakeHgrid.driver_name() == "make_hgrid" def test_MakeHgrid__runcmd(driverobj): diff --git a/src/uwtools/tests/drivers/test_make_solo_mosaic.py b/src/uwtools/tests/drivers/test_make_solo_mosaic.py index f4fd49339..bdd0f4573 100644 --- a/src/uwtools/tests/drivers/test_make_solo_mosaic.py +++ b/src/uwtools/tests/drivers/test_make_solo_mosaic.py @@ -71,7 +71,7 @@ def test_MakeSoloMosaic_provisioned_rundir(driverobj): def test_MakeSoloMosaic_driver_name(driverobj): - assert driverobj.driver_name == "make_solo_mosaic" + assert driverobj.driver_name() == MakeSoloMosaic.driver_name() == "make_solo_mosaic" def test_MakeSoloMosaic__runcmd(driverobj): diff --git a/src/uwtools/tests/drivers/test_mpas.py b/src/uwtools/tests/drivers/test_mpas.py index e0b252c49..b85a55588 100644 --- a/src/uwtools/tests/drivers/test_mpas.py +++ b/src/uwtools/tests/drivers/test_mpas.py @@ -237,7 +237,7 @@ def test_MPAS_provisioned_rundir(driverobj): def test_MPAS_driver_name(driverobj): - assert driverobj.driver_name == "mpas" + assert driverobj.driver_name() == MPAS.driver_name() == "mpas" def test_MPAS_streams_file(config, driverobj): diff --git a/src/uwtools/tests/drivers/test_mpas_init.py b/src/uwtools/tests/drivers/test_mpas_init.py index 679267d38..f9784755c 100644 --- a/src/uwtools/tests/drivers/test_mpas_init.py +++ b/src/uwtools/tests/drivers/test_mpas_init.py @@ -212,7 +212,7 @@ def test_MPASInit_provisioned_rundir(driverobj): def test_MPASInit_driver_name(driverobj): - assert driverobj.driver_name == "mpas_init" + assert driverobj.driver_name() == MPASInit.driver_name() == "mpas_init" def test_MPASInit_streams_file(config, driverobj): diff --git a/src/uwtools/tests/drivers/test_orog_gsl.py b/src/uwtools/tests/drivers/test_orog_gsl.py index 875f52fe2..a942aa687 100644 --- a/src/uwtools/tests/drivers/test_orog_gsl.py +++ b/src/uwtools/tests/drivers/test_orog_gsl.py @@ -103,7 +103,7 @@ def test_OrogGSL_topo_data_3os(driverobj): def test_OrogGSL_driver_name(driverobj): - assert driverobj.driver_name == "orog_gsl" + assert driverobj.driver_name() == OrogGSL.driver_name() == "orog_gsl" def test_OrogGSL__runcmd(driverobj): diff --git a/src/uwtools/tests/drivers/test_schism.py b/src/uwtools/tests/drivers/test_schism.py index d58fd0876..0c5fea845 100644 --- a/src/uwtools/tests/drivers/test_schism.py +++ b/src/uwtools/tests/drivers/test_schism.py @@ -72,4 +72,4 @@ def test_SCHISM_provisioned_rundir(driverobj): def test_SCHISM_driver_name(driverobj): - assert driverobj.driver_name == "schism" + assert driverobj.driver_name() == SCHISM.driver_name() == "schism" diff --git a/src/uwtools/tests/drivers/test_sfc_climo_gen.py b/src/uwtools/tests/drivers/test_sfc_climo_gen.py index f7e81c088..a9355f42d 100644 --- a/src/uwtools/tests/drivers/test_sfc_climo_gen.py +++ b/src/uwtools/tests/drivers/test_sfc_climo_gen.py @@ -139,4 +139,4 @@ def test_SfcClimoGen_provisioned_rundir(driverobj): def test_SfcClimoGen_driver_name(driverobj): - assert driverobj.driver_name == "sfc_climo_gen" + assert driverobj.driver_name() == SfcClimoGen.driver_name() == "sfc_climo_gen" diff --git a/src/uwtools/tests/drivers/test_shave.py b/src/uwtools/tests/drivers/test_shave.py index ec1ff4cc8..b6ac9c2a2 100644 --- a/src/uwtools/tests/drivers/test_shave.py +++ b/src/uwtools/tests/drivers/test_shave.py @@ -81,7 +81,7 @@ def test_Shave_provisioned_rundir(driverobj): def test_Shave_driver_name(driverobj): - assert driverobj.driver_name == "shave" + assert driverobj.driver_name() == Shave.driver_name() == "shave" def test_Shave__runcmd(driverobj): diff --git a/src/uwtools/tests/drivers/test_support.py b/src/uwtools/tests/drivers/test_support.py index aa643e728..d9cf9545b 100644 --- a/src/uwtools/tests/drivers/test_support.py +++ b/src/uwtools/tests/drivers/test_support.py @@ -59,8 +59,8 @@ def t2(self): def t3(self): "@tasks t3" - @property - def driver_name(self): + @classmethod + def driver_name(cls): pass @property diff --git a/src/uwtools/tests/drivers/test_ungrib.py b/src/uwtools/tests/drivers/test_ungrib.py index 52952fd3b..be2a15388 100644 --- a/src/uwtools/tests/drivers/test_ungrib.py +++ b/src/uwtools/tests/drivers/test_ungrib.py @@ -126,7 +126,7 @@ def test_Ungrib_vtable(driverobj): def test_Ungrib_driver_name(driverobj): - assert driverobj.driver_name == "ungrib" + assert driverobj.driver_name() == Ungrib.driver_name() == "ungrib" def test_Ungrib__gribfile(driverobj): diff --git a/src/uwtools/tests/drivers/test_upp.py b/src/uwtools/tests/drivers/test_upp.py index 0747504da..55b34cb52 100644 --- a/src/uwtools/tests/drivers/test_upp.py +++ b/src/uwtools/tests/drivers/test_upp.py @@ -168,7 +168,7 @@ def test_UPP_provisioned_rundir(driverobj): def test_UPP_driver_name(driverobj): - assert driverobj.driver_name == "upp" + assert driverobj.driver_name() == UPP.driver_name() == "upp" def test_UPP__namelist_path(driverobj): diff --git a/src/uwtools/tests/drivers/test_ww3.py b/src/uwtools/tests/drivers/test_ww3.py index bf97854f9..253080387 100644 --- a/src/uwtools/tests/drivers/test_ww3.py +++ b/src/uwtools/tests/drivers/test_ww3.py @@ -80,4 +80,4 @@ def test_WaveWatchIII_restart_directory(driverobj): def test_WaveWatchIII_driver_name(driverobj): - assert driverobj.driver_name == "ww3" + assert driverobj.driver_name() == WaveWatchIII.driver_name() == "ww3" diff --git a/src/uwtools/tests/fixtures/testdriver.py b/src/uwtools/tests/fixtures/testdriver.py index e0320644a..025a7df55 100644 --- a/src/uwtools/tests/fixtures/testdriver.py +++ b/src/uwtools/tests/fixtures/testdriver.py @@ -17,6 +17,6 @@ def eighty_eight(self): yield asset(88, lambda: True) yield None - @property - def driver_name(self): + @classmethod + def driver_name(cls): return "testdriver" diff --git a/src/uwtools/tests/support.py b/src/uwtools/tests/support.py index 456b1bc5e..41364ed67 100644 --- a/src/uwtools/tests/support.py +++ b/src/uwtools/tests/support.py @@ -13,8 +13,8 @@ def compare_files(path1: Union[Path, str], path2: Union[Path, str]) -> bool: """ - Determines whether the two given files are identical up to any number of trailing newlines, - which are ignored. Print the contents of both files when they do not match. + Determine whether the two given files are identical up to any number of trailing newlines, which + are ignored. Print the contents of both files when they do not match. :param path1: Path to first file. :param path2: Path to second file. diff --git a/src/uwtools/tests/test_cli.py b/src/uwtools/tests/test_cli.py index d1c83000f..09bab82e0 100644 --- a/src/uwtools/tests/test_cli.py +++ b/src/uwtools/tests/test_cli.py @@ -7,6 +7,7 @@ from argparse import ArgumentParser as Parser from argparse import _SubParsersAction from pathlib import Path +from textwrap import dedent from unittest.mock import Mock, patch from pytest import fixture, mark, raises @@ -570,7 +571,6 @@ def test__dispatch_template_translate_no_optional(): @mark.parametrize("hours", [0, 24, 168]) def test__dispatch_to_driver(hours): - name = "adriver" cycle = dt.datetime.now() leadtime = dt.timedelta(hours=hours) args: dict = { @@ -582,11 +582,12 @@ def test__dispatch_to_driver(hours): "dry_run": False, "graph_file": None, "key_path": ["foo", "bar"], + "show_schema": False, "stdin_ok": True, } adriver = Mock() with patch.object(cli, "import_module", return_value=adriver): - cli._dispatch_to_driver(name=name, args=args) + cli._dispatch_to_driver(name="adriver", args=args) adriver.execute.assert_called_once_with( batch=True, config="/path/to/config", @@ -600,6 +601,30 @@ def test__dispatch_to_driver(hours): ) +def test__dispatch_to_driver_no_schema(capsys): + adriver = Mock() + with patch.object(cli, "import_module", return_value=adriver): + with raises(SystemExit): + cli._dispatch_to_driver(name="adriver", args={}) + assert "No TASK specified" in capsys.readouterr().err + + +def test__dispatch_to_driver_show_schema(capsys): + adriver = Mock() + adriver.schema.return_value = {"fruit": {"b": "banana", "a": "apple"}} + with patch.object(cli, "import_module", return_value=adriver): + assert cli._dispatch_to_driver(name="adriver", args={"show_schema": True}) is True + expected = """ + { + "fruit": { + "a": "apple", + "b": "banana" + } + } + """ + assert capsys.readouterr().out == dedent(expected).lstrip() + + @mark.parametrize("quiet", [False, True]) @mark.parametrize("verbose", [False, True]) def test_main_fail_checks(capsys, quiet, verbose):