diff --git a/changes.d/6892.feat.md b/changes.d/6892.feat.md
new file mode 100644
index 00000000000..4ca1eda92a9
--- /dev/null
+++ b/changes.d/6892.feat.md
@@ -0,0 +1 @@
+Added `--format=json` option to `cylc broadcast` for use with the `--display` option. Deprecated the `--raw` option in favour of `--format=raw`.
diff --git a/cylc/flow/scripts/broadcast.py b/cylc/flow/scripts/broadcast.py
index 86b96cc39d6..b9ae72863cb 100755
--- a/cylc/flow/scripts/broadcast.py
+++ b/cylc/flow/scripts/broadcast.py
@@ -111,6 +111,11 @@
from optparse import Values
+RAW_DEPR_MSG = (
+ "DEPRECATED: the --raw option will be removed at Cylc 8.7; "
+ "use --format=raw instead."
+)
+
REC_ITEM = re.compile(r'^\[([^\]]*)\](.*)$')
MUTATION = '''
@@ -312,12 +317,33 @@ def get_option_parser() -> COP:
help="Use unicode box characters with -d, -k.",
action="store_true", default=False, dest="unicode")
+ # BACK COMPAT: --raw
+ # From: < 8.5.1
+ # To: 8.5.1
+ # Remove at: 8.7.0
parser.add_option(
"-r", "--raw",
- help="With -d/--display or -k/--display-task, write out "
- "the broadcast config structure in raw Python form.",
+ help=(
+ "With -d/--display or -k/--display-task, write out "
+ "the broadcast config structure in raw Python form. "
+ f"{RAW_DEPR_MSG}"
+ ),
action="store_true", default=False, dest="raw")
+ parser.add_option(
+ '--format',
+ help=(
+ "With -d/--display or -k/--display-task, write out "
+ "the broadcast config structure in one of the following formats: "
+ "tree, json, or raw (like json but as a Python dictionary). "
+ r"Default: %default."
+ ),
+ action='store',
+ dest='format',
+ choices=('tree', 'json', 'raw'),
+ default='tree',
+ )
+
return parser
@@ -356,6 +382,9 @@ async def run(options: 'Values', workflow_id):
}
+ if options.raw:
+ ret['stderr'].append(cparse(f"{RAW_DEPR_MSG}"))
+
if options.show or options.showtask:
if options.showtask:
try:
@@ -369,7 +398,12 @@ async def run(options: 'Values', workflow_id):
for wflow in result['workflows']:
settings = wflow['broadcasts']
padding = get_padding(settings) * ' '
- if options.raw:
+ if options.format == 'json':
+ import json
+ ret['stdout'].append(
+ json.dumps(settings, indent=2, sort_keys=True)
+ )
+ elif options.raw or options.format == 'raw':
ret['stdout'].append(str(settings))
else:
ret['stdout'].extend(
diff --git a/tests/functional/broadcast/14-display-format.t b/tests/functional/broadcast/14-display-format.t
new file mode 100644
index 00000000000..239d10827a4
--- /dev/null
+++ b/tests/functional/broadcast/14-display-format.t
@@ -0,0 +1,76 @@
+#!/usr/bin/env bash
+# THIS FILE IS PART OF THE CYLC WORKFLOW ENGINE.
+# Copyright (C) NIWA & British Crown (Met Office) & Contributors.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+# Test the --format option.
+
+. "$(dirname "$0")/test_header"
+set_test_number 6
+
+init_workflow "${TEST_NAME_BASE}" << '__EOF__'
+[scheduler]
+ [[events]]
+ stall timeout = PT0S
+ abort on stall timeout = True
+[scheduling]
+ [[graph]]
+ R1 = foo
+[runtime]
+ [[foo]]
+ pre-script = """
+ cylc broadcast "$CYLC_WORKFLOW_ID" \
+ -p "$CYLC_TASK_CYCLE_POINT" -n "$CYLC_TASK_NAME" \
+ --set '[environment]horse=dorothy' \
+ --set 'post-script=echo "$horse"'
+ """
+ script = """
+ cylc broadcast "$CYLC_WORKFLOW_ID" --display --format json > out.json
+ cylc broadcast "$CYLC_WORKFLOW_ID" --display --format raw > raw1.stdout
+ # Test deprecated option:
+ cylc broadcast "$CYLC_WORKFLOW_ID" --display --raw 1> raw2.stdout 2> raw2.stderr
+ """
+__EOF__
+
+run_ok "${TEST_NAME_BASE}-validate" cylc validate "${WORKFLOW_NAME}"
+workflow_run_ok "${TEST_NAME_BASE}-run" cylc play "${WORKFLOW_NAME}" --no-detach
+
+FOO_WORK_DIR="${WORKFLOW_RUN_DIR}/work/1/foo"
+
+TEST_NAME="${TEST_NAME_BASE}-cmp-json"
+cmp_json "$TEST_NAME" "${FOO_WORK_DIR}/out.json" << '__EOF__'
+{
+ "1": {
+ "foo": {
+ "environment": {
+ "horse": "dorothy"
+ },
+ "post-script": "echo \"$horse\""
+ }
+ }
+}
+__EOF__
+
+cmp_ok "${FOO_WORK_DIR}/raw1.stdout" << '__EOF__'
+{'1': {'foo': {'environment': {'horse': 'dorothy'}, 'post-script': 'echo "$horse"'}}}
+__EOF__
+
+cmp_ok "${FOO_WORK_DIR}/raw2.stdout" "${FOO_WORK_DIR}/raw1.stdout"
+
+cmp_ok "${FOO_WORK_DIR}/raw2.stderr" << __EOF__
+DEPRECATED: the --raw option will be removed at Cylc 8.7; use --format=raw instead.
+__EOF__
+
+purge