Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cat-log - open job logs in editor #2260

Merged
merged 10 commits into from
May 12, 2017
60 changes: 60 additions & 0 deletions bin/cylc-cat-log
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ if remrun().execute():
sys.exit(0)

import os
import re
from tempfile import NamedTemporaryFile
from pipes import quote
import shlex
from subprocess import Popen, PIPE
Expand All @@ -41,6 +43,7 @@ from cylc.suite_host import is_remote_host
from cylc.suite_logging import get_logs
from cylc.cfgspec.globalcfg import GLOBAL_CFG
from cylc.task_id import TaskID
from parsec.fileparse import read_and_proc


NAME_DEFAULT = "log"
Expand Down Expand Up @@ -123,6 +126,16 @@ def get_option_parser():
help="Task job log only: List log directory on the job host",
action="store_const", const=LIST_MODE_REMOTE, dest="list_mode")

parser.add_option(
"-g", "--geditor",
help="force use of the configured GUI editor.",
action="store_true", default=False, dest="geditor")

parser.add_option(
"-b", "--teditor",
help="force use of the configured Non-GUI editor.",
action="store_true", default=False, dest="editor")

return parser


Expand Down Expand Up @@ -271,6 +284,32 @@ def main():
else:
owner, host = (None, user_at_host)

cylc_tmpdir = GLOBAL_CFG.get_tmpdir()
if options.geditor:
editor = GLOBAL_CFG.get(['editors', 'gui'])
elif options.editor:
editor = GLOBAL_CFG.get(['editors', 'terminal'])

if options.editor or options.geditor:
if os.path.exists(filename):
lines = read_and_proc(filename)
else:
print >> sys.stderr, 'No such file or directory: %s' % filename
sys.exit(1)

viewfile = NamedTemporaryFile(
suffix='.' + os.path.basename(filename),
prefix=suite.replace('/', '_') + '.', dir=cylc_tmpdir
)

for line in lines:
viewfile.write(line + '\n')
viewfile.seek(0, 0)

os.chmod(viewfile.name, 0400)

modtime1 = os.stat(viewfile.name).st_mtime

# Construct the shell command
commands = []
if options.location_mode:
Expand All @@ -296,6 +335,10 @@ def main():
cmd_tmpl = str(GLOBAL_CFG.get_host_item(
"local tail command template"))
commands.append(shlex.split(cmd_tmpl % {"filename": filename}))
elif options.geditor or options.editor:
command_list = shlex.split(editor)
command_list.append(viewfile.name)
commands.append(command_list)
else:
commands.append(["cat", filename])

Expand All @@ -308,15 +351,32 @@ def main():
err = None
for command in commands:
stderr = PIPE

if options.debug:
sys.stderr.write(
" ".join([quote(item) for item in command]) + "\n")
stderr = None
proc = Popen(command, stderr=stderr)
err = proc.communicate()[1]
ret_code = proc.wait()

if options.editor or options.geditor:
if ret_code != 0:
print >>sys.stderr, command, 'failed:', ret_code
sys.exit(1)

modtime2 = os.stat(viewfile.name).st_mtime

if modtime2 > modtime1:
print >> sys.stderr, (
'WARNING: YOU HAVE EDITED A TEMPORARY READ_ONLY COPY : ')
print >> sys.stderr, viewfile.name

viewfile.close()

if ret_code == 0:
break

if ret_code and err:
sys.stderr.write(err)
sys.exit(ret_code)
Expand Down
4 changes: 3 additions & 1 deletion bin/cylc-view
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ def main():

# write to a temporary file
viewfile = NamedTemporaryFile(
suffix=".suite.rc", prefix=suite + '.', dir=cylc_tmpdir)
suffix=".suite.rc", prefix=suite.replace('/', '_') + '.',
dir=cylc_tmpdir
)
for line in lines:
viewfile.write(line + '\n')
viewfile.seek(0, 0)
Expand Down
2 changes: 1 addition & 1 deletion lib/cylc/cycling/iso8601.py
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ def test_multiple_exclusions_complex2(self):
'20000101T0500Z'])

def test_multiple_exclusions_simple(self):
"""Tests the generation of points for sequences with multiple exclusions
"""Tests generation of points for sequences with multiple exclusions
"""
init(time_zone='Z')
sequence = ISO8601Sequence('PT1H!(20000101T02Z,20000101T03Z)',
Expand Down
73 changes: 72 additions & 1 deletion lib/cylc/gui/app_gcylc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,36 @@ def view_task_descr(self, w, e, task_id, *args):
self.gcapture_windows.append(foo)
foo.run()

def view_in_editor(self, w, e, task_id, choice):
try:
task_state_summary = self.updater.full_state_summary[task_id]
except KeyError:
warning_dialog('%s is not live' % task_id, self.window).warn()
return False
if (not task_state_summary['logfiles'] and
not task_state_summary.get('job_hosts')):
warning_dialog('%s has no log files' % task_id, self.window).warn()
else:
if choice == 'job-activity.log':
command_opt = "--activity"
elif choice == 'job.status':
command_opt = "--status"
elif choice == 'job.out':
command_opt = "--stdout"
elif choice == 'job.err':
command_opt = "--stderr"
elif choice == 'job':
command_opt = ""

command = (
"cylc cat-log %s --geditor %s %s" % (
command_opt, self.cfg.suite, task_id)
)

foo = gcapture_tmpfile(command, self.cfg.cylc_tmpdir, 400, 400)
self.gcapture_windows.append(foo)
foo.run()

def view_task_info(self, w, e, task_id, choice):
try:
task_state_summary = self.updater.full_state_summary[task_id]
Expand Down Expand Up @@ -1368,7 +1398,48 @@ def get_right_click_menu(self, task_ids, t_states, task_is_family=False,
# help_menu.append(cug_pdf_item)
# cug_pdf_item.connect('activate', self.browse, '--pdf')

# Separator.
# View In Editor.
view_editor_menu = gtk.Menu()
view_editor_item = gtk.ImageMenuItem("View In Editor")
img = gtk.image_new_from_stock(gtk.STOCK_DIALOG_INFO,
gtk.ICON_SIZE_MENU)
view_editor_item.set_image(img)
view_editor_item.set_submenu(view_editor_menu)
menu.append(view_editor_item)

# NOTE: we have to respond to 'button-release-event' rather
# than 'activate' in order for sub-menus to work in the
# graph-view so use connect_right_click_sub_menu instead of
# item.connect

for key, filename in [
('job script', 'job'),
('job activity log', 'job-activity.log'),
('job status file', 'job.status')]:
item = gtk.ImageMenuItem(key)
item.set_image(gtk.image_new_from_stock(
gtk.STOCK_DND, gtk.ICON_SIZE_MENU))
view_editor_menu.append(item)
self.connect_right_click_sub_menu(is_graph_view, item,
self.view_in_editor,
task_ids[0], filename)
item.set_sensitive(
t_states[0] in TASK_STATUSES_WITH_JOB_SCRIPT)

for key, filename in [
('job stdout', 'job.out'),
('job stderr', 'job.err')]:
item = gtk.ImageMenuItem(key)
item.set_image(gtk.image_new_from_stock(
gtk.STOCK_DND, gtk.ICON_SIZE_MENU))
view_editor_menu.append(item)
self.connect_right_click_sub_menu(is_graph_view, item,
self.view_in_editor,
task_ids[0], filename)
item.set_sensitive(
t_states[0] in TASK_STATUSES_WITH_JOB_LOGS)

# Separator
menu.append(gtk.SeparatorMenuItem())

# Trigger (run now).
Expand Down