diff --git a/CHANGES.md b/CHANGES.md index 3fb55045e3f..e89c7613c2c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -53,6 +53,12 @@ Jinja2 used by Cylc from 2.11 to 3.0. [#4896](https://github.com/cylc/cylc-flow/pull/4896) - Allow the setting of default job runner directives for platforms. +[#4993](https://github.com/cylc/cylc-flow/pull/4993) - Remove the few remaining +uses of a configured text editor (via `cylc view` and `cylc cat-log` options). +The primary uses of it (`cylc trigger --edit` and `cylc edit` in Cylc 7) have +already been removed from Cylc 8. + + ### Fixes [#4984](https://github.com/cylc/cylc-flow/pull/4984) - diff --git a/cylc/flow/cfgspec/globalcfg.py b/cylc/flow/cfgspec/globalcfg.py index d998b697cea..567ebf7dd72 100644 --- a/cylc/flow/cfgspec/globalcfg.py +++ b/cylc/flow/cfgspec/globalcfg.py @@ -1075,55 +1075,6 @@ def default_for( .. versionadded:: 8.0.0 """) - - with Conf('editors', desc=''' - Choose your favourite text editor for editing workflow configurations. - '''): - Conf('terminal', VDR.V_STRING, desc=''' - An in-terminal text editor to be used by the Cylc command line. - - If unspecified Cylc will use the environment variable - ``$EDITOR`` which is the preferred way to set your text editor. - - .. Note:: - - You can set your ``$EDITOR`` in your shell profile file - (e.g. ``~.bashrc``) - - If neither this or ``$EDITOR`` are specified then Cylc will - default to ``vi``. - - Examples:: - - ed - emacs -nw - nano - vi - ''') - Conf('gui', VDR.V_STRING, desc=''' - A graphical text editor to be used by cylc. - - If unspecified Cylc will use the environment variable - ``$GEDITOR`` which is the preferred way to set your text editor. - - .. Note:: - - You can set your ``$GEDITOR`` in your shell profile file - (e.g. ``~.bashrc``) - - If neither this or ``$GEDITOR`` are specified then Cylc will - default to ``gvim -fg``. - - Examples:: - - atom --wait - code --new-window --wait - emacs - gedit -s - gvim -fg - nedit - ''') - with Conf('platforms', desc=''' Platforms allow you to define compute resources available at your site. @@ -1924,7 +1875,6 @@ def load(self) -> None: # Flesh out with defaults self.expand() - self._set_default_editors() self._no_platform_group_name_overlap() with suppress(KeyError): validate_platforms(self.sparse['platforms']) @@ -1946,15 +1896,6 @@ def _validate_source_dirs(self) -> None: keys, value=item, msg="must be an absolute path" ) - def _set_default_editors(self): - # default to $[G]EDITOR unless an editor is defined in the config - # NOTE: use `or` to handle cases where an env var is set to '' - cfg = self.get(sparse=False) - if not cfg['editors']['terminal']: - cfg['editors']['terminal'] = os.environ.get('EDITOR') or 'vi' - if not cfg['editors']['gui']: - cfg['editors']['gui'] = os.environ.get('GEDITOR') or 'gvim -fg' - def _no_platform_group_name_overlap(self): if ( 'platforms' in self.sparse and diff --git a/cylc/flow/scripts/cat_log.py b/cylc/flow/scripts/cat_log.py index 74e4ab42198..b0cb747a2ad 100755 --- a/cylc/flow/scripts/cat_log.py +++ b/cylc/flow/scripts/cat_log.py @@ -20,9 +20,9 @@ View Cylc workflow and job log files. -Print, view-in-editor, or tail-follow content, print path, or list directory, -of local or remote task job and scheduler logs. Job runner view commands -(e.g. 'qcat') are used if defined in global config and the job is running. +Print, tail-follow, print path, or list directory, of local or remote task job +and scheduler logs. Job runner view commands (e.g. 'qcat') are used if defined +in global config and the job is running. For standard log types use the short-cut option argument or full filename (e.g. for job stdout "-f o" or "-f job.out" will do). @@ -57,13 +57,10 @@ from glob import glob from pathlib import Path import shlex -from stat import S_IRUSR from subprocess import Popen, PIPE, DEVNULL import sys -from tempfile import NamedTemporaryFile from typing import TYPE_CHECKING -from cylc.flow.cfgspec.glbl_cfg import glbl_cfg from cylc.flow.exceptions import InputError import cylc.flow.flags from cylc.flow.hostuserutil import is_remote_platform @@ -121,7 +118,6 @@ 'd': 'print-dir', 'c': 'cat', 't': 'tail', - 'e': 'edit' } @@ -185,20 +181,8 @@ def view_log(logpath, mode, tailer_tmpl, batchview_cmd=None, remote=False, for entry in sorted(os.listdir(os.path.dirname(logpath))): print(entry) return 0 - elif not remote and mode == 'edit': - # Copy the log to a temporary read-only file for viewing in editor. - # Copy only BUFSIZE bytes at time, in case the file is huge. - outfile = NamedTemporaryFile() - with open(logpath, 'rb') as log: - data = log.read(BUFSIZE) - while data: - outfile.write(data) - data = log.read(BUFSIZE) - os.chmod(outfile.name, S_IRUSR) - outfile.seek(0, 0) - return outfile - elif mode == 'cat' or (remote and mode == 'edit'): - # Just cat file contents to stdout. + elif mode == 'cat': + # print file contents to stdout. if batchview_cmd is not None: cmd = shlex.split(batchview_cmd) else: @@ -264,11 +248,6 @@ def get_option_parser() -> COP: help="Job submit number (default=%s, i.e. latest)." % NN, metavar="INT", action="store", dest="submit_num", default=NN) - parser.add_option( - "-g", "--geditor", - help="edit mode: use your configured GUI editor.", - action="store_true", default=False, dest="geditor") - parser.add_option( "--remote-arg", help="(for internal use: continue processing on job host)", @@ -300,33 +279,6 @@ def get_task_job_attrs(workflow_id, point, task, submit_num): return (task_job_data["platform_name"], job_runner_name, live_job_id) -def tmpfile_edit(tmpfile, geditor=False): - """Edit a temporary read-only file containing the string filestr. - - Detect and warn if the user forcibly writes to the temporary file. - - """ - if geditor: - editor = glbl_cfg().get(['editors', 'gui']) - else: - editor = glbl_cfg().get(['editors', 'terminal']) - modtime1 = os.stat(tmpfile.name).st_mtime - cmd = shlex.split(editor) - cmd.append(tmpfile.name) - proc = Popen(cmd, stderr=PIPE) # nosec - # * editor command is user configurable - err = proc.communicate()[1].decode() - ret_code = proc.wait() - if ret_code == 0 and os.stat(tmpfile.name).st_mtime > modtime1: - sys.stderr.write( - 'WARNING: you edited a TEMPORARY COPY of %s\n' % ( - os.path.basename(tmpfile.name) - ) - ) - if ret_code and err: - sys.stderr.write(err) - - @cli_function(get_option_parser) def main( parser: COP, @@ -337,7 +289,7 @@ def main( """Implement cylc cat-log CLI. Determine log path, user@host, batchview_cmd, and action (print, dir-list, - cat, edit, or tail), and then if the log path is: + cat, or tail), and then if the log path is: a) local: perform action on log path, or b) remote: re-invoke cylc cat-log as a) on the remote account @@ -387,8 +339,6 @@ def main( out = view_log(logpath, mode, tail_tmpl, color=color) if out == 1: sys.exit(1) - if mode == 'edit': - tmpfile_edit(out, options.geditor) return else: @@ -452,29 +402,15 @@ def main( if batchview_cmd: cmd.append('--remote-arg=%s' % shlex.quote(batchview_cmd)) cmd.append(workflow_id) - is_edit_mode = (mode == 'edit') # TODO: Add Intelligent Host selection to this - try: - proc = remote_cylc_cmd( + with suppress(KeyboardInterrupt): + # (Ctrl-C while tailing) + remote_cylc_cmd( cmd, platform, - capture_process=is_edit_mode, + capture_process=False, manage=(mode == 'tail') ) - except KeyboardInterrupt: - # Ctrl-C while tailing. - pass - else: - if is_edit_mode: - # Write remote stdout to a temp file for viewing in editor. - # Only BUFSIZE bytes at a time in case huge stdout volume. - out = NamedTemporaryFile() - data = proc.stdout.read(BUFSIZE) - while data: - out.write(data) - data = proc.stdout.read(BUFSIZE) - os.chmod(out.name, S_IRUSR) - out.seek(0, 0) else: # Local task job or local job log. logpath = os.path.normpath(get_workflow_run_job_dir( @@ -483,7 +419,4 @@ def main( tail_tmpl = os.path.expandvars(platform["tail command template"]) out = view_log(logpath, mode, tail_tmpl, batchview_cmd, color=color) - if mode != 'edit': - sys.exit(out) - if mode == 'edit': - tmpfile_edit(out, options.geditor) + sys.exit(out) diff --git a/cylc/flow/scripts/view.py b/cylc/flow/scripts/view.py index 0cd95d09f9e..f5afbf0bb9e 100755 --- a/cylc/flow/scripts/view.py +++ b/cylc/flow/scripts/view.py @@ -18,29 +18,15 @@ """cylc view [OPTIONS] ARGS -View a processed workflow configuration. +Print a processed workflow configuration. Note: This is different to `cylc config` which displays the parsed configuration (as Cylc would see it). - -View a read-only temporary copy of workflow NAME's flow.cylc file, in your -editor, after optional include-file inlining and Jinja2 preprocessing. - -The edit process is spawned in the foreground as follows: - $ flow.cylc -Where can be set in cylc global config. """ -import os -import shlex -from subprocess import call -import sys -from tempfile import NamedTemporaryFile from typing import TYPE_CHECKING -from cylc.flow.cfgspec.glbl_cfg import glbl_cfg -from cylc.flow.exceptions import CylcError from cylc.flow.id_cli import parse_id from cylc.flow.option_parsers import ( WORKFLOW_ID_OR_PATH_ARG_DOC, @@ -107,14 +93,6 @@ def get_option_parser(): "not correspond to those reported by the parser).", action="store_true", default=False, dest="cat") - parser.add_option( - "--gui", "-g", help="Force use of the configured GUI editor.", - action="store_true", default=False, dest="geditor") - - parser.add_option( - "--stdout", help="Print the workflow definition to stdout.", - action="store_true", default=False, dest="stdout") - return parser @@ -126,11 +104,6 @@ def main(parser: COP, options: 'Values', workflow_id: str) -> None: constraint='workflows', ) - if options.geditor: - editor = glbl_cfg().get(['editors', 'gui']) - else: - editor = glbl_cfg().get(['editors', 'terminal']) - # read in the flow.cylc file viewcfg = { 'mark': options.mark, @@ -142,51 +115,11 @@ def main(parser: COP, options: 'Values', workflow_id: str) -> None: 'inline': (options.inline or options.jinja2 or options.empy or options.process), } - lines = read_and_proc( + for line in read_and_proc( flow_file, - load_template_vars(options.templatevars, options.templatevars_file), - viewcfg=viewcfg) - - if options.stdout: - for line in lines: - print(line) - sys.exit(0) - - # write to a temporary file - viewfile = NamedTemporaryFile( - suffix=".flow.cylc", prefix=workflow_id.replace('/', '_') + '.', - ) - for line in lines: - viewfile.write((line + '\n').encode()) - viewfile.seek(0, 0) - - # set the file to be read only - os.chmod(viewfile.name, 0o400) - - # capture the temp file's mod time in case the user edits it - # and overrides the readonly mode. - modtime1 = os.stat(viewfile.name).st_mtime - - # in case editor has options, e.g. 'emacs -nw': - command_list = shlex.split(editor) - command_list.append(viewfile.name) - command = ' '.join(command_list) - # THIS BLOCKS UNTIL THE COMMAND COMPLETES - retcode = call(command_list) # nosec (editor command is user configurable) - if retcode != 0: - # the command returned non-zero exist status - raise CylcError(f'{command} failed: {retcode}') - - # !!!VIEWING FINISHED!!! - - # Did the user edit the file - modtime2 = os.stat(viewfile.name).st_mtime - - if modtime2 > modtime1: - print( - "\nWARNING: YOU HAVE EDITED A TEMPORARY READ-ONLY COPY " - f"OF THE WORKFLOW:\n {viewfile.name}\n", - file=sys.stderr - ) - # DONE - viewfile.close() + load_template_vars( + options.templatevars, options.templatevars_file + ), + viewcfg=viewcfg + ): + print(line) diff --git a/tests/functional/cylc-cat-log/07-editor.t b/tests/functional/cylc-cat-log/07-editor.t deleted file mode 100755 index 82b705e9b8c..00000000000 --- a/tests/functional/cylc-cat-log/07-editor.t +++ /dev/null @@ -1,26 +0,0 @@ -#!/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 "cylc cat-log" open local logs in editor. - -. "$(dirname "$0")/test_header" -. "${TEST_SOURCE_DIR}/editor/bin/run_tests.sh" -export PATH="${TEST_SOURCE_DIR}/editor/bin/:${PATH}" - -install_workflow "${TEST_NAME_BASE}" "editor" -run_tests localhost -purge diff --git a/tests/functional/cylc-cat-log/08-editor-remote.t b/tests/functional/cylc-cat-log/08-editor-remote.t deleted file mode 100755 index b85617dc113..00000000000 --- a/tests/functional/cylc-cat-log/08-editor-remote.t +++ /dev/null @@ -1,27 +0,0 @@ -#!/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 "cylc cat-log" open local logs in editor. -export REQUIRE_PLATFORM='loc:remote' -. "$(dirname "$0")/test_header" - -. "${TEST_SOURCE_DIR}/editor/bin/run_tests.sh" -export PATH="${TEST_SOURCE_DIR}/editor/bin/:${PATH}" - -install_workflow "${TEST_NAME_BASE}" "editor" -run_tests "${CYLC_TEST_PLATFORM}" -purge diff --git a/tests/functional/cylc-cat-log/editor/bin/my-editor b/tests/functional/cylc-cat-log/editor/bin/my-editor deleted file mode 100755 index 186f7e4d3ec..00000000000 --- a/tests/functional/cylc-cat-log/editor/bin/my-editor +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -cp "$@" "$DESTFILE" diff --git a/tests/functional/cylc-cat-log/editor/bin/run_tests.sh b/tests/functional/cylc-cat-log/editor/bin/run_tests.sh deleted file mode 100644 index f7c6ce7fbfd..00000000000 --- a/tests/functional/cylc-cat-log/editor/bin/run_tests.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/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 . - -set_test_number 16 - -# Configure a fake editor that just copies a job file to ${DESTFILE}. -create_test_global_config '' ' -[editors] - terminal = my-editor - gui = my-editor' - -function run_tests { - PLATFORM=$1 - - TEST_NAME="${TEST_NAME_BASE}-validate" - run_ok "${TEST_NAME}" cylc validate --set="PLATFORM='$PLATFORM'" \ - "${WORKFLOW_NAME}" - - # Run the workflow to generate some log files. - TEST_NAME="${TEST_NAME_BASE}-workflow-run" - run_ok "${TEST_NAME}" cylc play --set="PLATFORM='$PLATFORM'" \ - --no-detach "${WORKFLOW_NAME}" - - LOG_DIR="$RUN_DIR/${WORKFLOW_NAME}" - JOB_LOG_DIR="${LOG_DIR}/log/job/1/foo/01" - - for JOBFILE in "job" "job.out" "job.err" "job.status" "job-activity.log" \ - "job.custom"; do - export DESTFILE="${JOBFILE}.edit" - export ORIGFILE="${JOBFILE}.orig" - # Check we can view the job log file in the "editor". - TEST_NAME="${TEST_NAME_BASE}-${JOBFILE}" - run_ok "${TEST_NAME}" cylc cat-log -f "${JOBFILE}" -m e \ - "${WORKFLOW_NAME}//1/foo" - # Compare viewed (i.e copied by the fake editor) file with the original. - # (The original must be catted as it could be a remote log file). - cylc cat-log -f "${JOBFILE}" -m c \ - "${WORKFLOW_NAME}//1/foo" > "${ORIGFILE}" - cmp_ok "${DESTFILE}" "${ORIGFILE}" - done - - # Finally, test --geditor on the 'job' file. - TEST_NAME="${TEST_NAME_BASE}-job" - JOBFILE="job" - export DESTFILE="${JOBFILE}.edit" - run_ok "${TEST_NAME}" cylc cat-log -m e --geditor -f j \ - "${WORKFLOW_NAME}//1/foo" - cmp_ok "${DESTFILE}" "${JOB_LOG_DIR}/${JOBFILE}" -} diff --git a/tests/functional/cylc-cat-log/editor/flow.cylc b/tests/functional/cylc-cat-log/editor/flow.cylc deleted file mode 100644 index c7da5810375..00000000000 --- a/tests/functional/cylc-cat-log/editor/flow.cylc +++ /dev/null @@ -1,20 +0,0 @@ -#!jinja2 -[scheduler] - [[events]] - abort on stall timeout = True - stall timeout = PT30S -[scheduling] - [[graph]] - R1 = foo -[runtime] - [[foo]] - script = """ -echo 'Hello to stdout' ->&2 echo 'Hello to stderr' -cat >> ${CYLC_TASK_LOG_ROOT}.custom <<__END__ -the quick brown fox -jumped over -the lazy dog -__END__ -""" - platform = {{ PLATFORM | default('localhost') }} diff --git a/tests/functional/cylc-view/00-single-inc.t b/tests/functional/cylc-view/00-single-inc.t index fe2588fa614..faa5dc524ed 100755 --- a/tests/functional/cylc-view/00-single-inc.t +++ b/tests/functional/cylc-view/00-single-inc.t @@ -27,7 +27,7 @@ run_ok "${TEST_NAME}" cylc validate "${WORKFLOW_NAME}" #------------------------------------------------------------------------------- # Just inline TEST_NAME=${TEST_NAME_BASE}-inline -cylc view -i --stdout "${WORKFLOW_NAME}" > tmp.stdout +cylc view -i "${WORKFLOW_NAME}" > tmp.stdout cmp_ok tmp.stdout << EOF #!jinja2 [meta] @@ -56,7 +56,7 @@ EOF #------------------------------------------------------------------------------- # "cylc view -j/--jinja2" should imply "-i/inline" too: TEST_NAME=${TEST_NAME_BASE}-jinja2 -cylc view -j --stdout "${WORKFLOW_NAME}" > tmp.stdout +cylc view -j "${WORKFLOW_NAME}" > tmp.stdout cmp_ok tmp.stdout << EOF [meta] title = "Jinja2 simple ensemble example" @@ -80,7 +80,7 @@ EOF #------------------------------------------------------------------------------- # line continuation joining TEST_NAME=${TEST_NAME_BASE}-continuation -cylc view -c --stdout "${WORKFLOW_NAME}" > tmp.stdout +cylc view -c "${WORKFLOW_NAME}" > tmp.stdout cmp_ok tmp.stdout << EOF #!jinja2 [meta] @@ -108,7 +108,7 @@ EOF #------------------------------------------------------------------------------- # all processing TEST_NAME=${TEST_NAME_BASE}-process -cylc view -p --stdout "${WORKFLOW_NAME}" > tmp.stdout +cylc view -p "${WORKFLOW_NAME}" > tmp.stdout cmp_ok tmp.stdout << EOF [meta] title = "Jinja2 simple ensemble example" diff --git a/tests/functional/include-files/00-basic.t b/tests/functional/include-files/00-basic.t index 7ad1ed2f63a..19fe3a9531b 100644 --- a/tests/functional/include-files/00-basic.t +++ b/tests/functional/include-files/00-basic.t @@ -28,7 +28,7 @@ run_ok "${TEST_NAME}.1" cylc val "${WORKFLOW_NAME}" # test workflow validates as inlined during editing mkdir inlined -cylc view --inline --stdout "${WORKFLOW_NAME}" > inlined/flow.cylc +cylc view --inline "${WORKFLOW_NAME}" > inlined/flow.cylc run_ok "${TEST_NAME}.2" cylc val ./inlined #------------------------------------------------------------------------------- # compare inlined workflow def with reference copy diff --git a/tests/functional/rnd/01-editors.t b/tests/functional/rnd/01-editors.t deleted file mode 100644 index 80b4150fac5..00000000000 --- a/tests/functional/rnd/01-editors.t +++ /dev/null @@ -1,58 +0,0 @@ -#!/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 that the cylc [editors] can be set via the config or envvars. -. "$(dirname "$0")/test_header" -set_test_number 6 -#------------------------------------------------------------------------------- - -# editors not set in the config or with envvars -TEST_NAME="$TEST_NAME_BASE-defaults" -export EDITOR= -export GEDITOR= -run_ok "$TEST_NAME" cylc config -d -i '[editors]' -cmp_ok "${TEST_NAME}.stdout" << __HERE__ -terminal = vi -gui = gvim -fg -__HERE__ - -# editors set with envvars -TEST_NAME="$TEST_NAME_BASE-envvar-override" -export EDITOR=editor -export GEDITOR=geditor -run_ok "$TEST_NAME" cylc config -d -i '[editors]' -cmp_ok "${TEST_NAME}.stdout" << __HERE__ -terminal = editor -gui = geditor -__HERE__ - -# editors set with envvars and the config (which should take precedence) -TEST_NAME="$TEST_NAME_BASE-config-override" -export EDITOR=editor -export GEDITOR=geditor -create_test_global_config '' ' -[editors] - terminal = myeditor - gui = mygeditor -' -run_ok "$TEST_NAME" cylc config -d -i '[editors]' -cmp_ok "${TEST_NAME}.stdout" << __HERE__ -terminal = myeditor -gui = mygeditor -__HERE__ - -exit