rbbt-util 6.0.3 → 6.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +1 -1
- data/bin/rbbt_exec.rb +2 -2
- data/lib/rbbt/tsv/excel.rb +1 -1
- data/lib/rbbt/util/filecache.rb +1 -1
- data/lib/rbbt/util/migrate.rb +1 -1
- data/lib/rbbt/util/misc/system.rb +92 -90
- data/lib/rbbt/workflow/refactor/export.rb +66 -66
- data/lib/rbbt/workflow/refactor/recursive.rb +64 -64
- data/lib/rbbt/workflow/refactor/task_info.rb +66 -65
- data/lib/rbbt/workflow/refactor.rb +0 -3
- data/lib/rbbt/workflow/remote_workflow/driver/rest.rb +1 -2
- data/lib/rbbt-util.rb +2 -2
- data/python/rbbt/__init__.py +78 -4
- data/python/rbbt/workflow/remote.py +104 -0
- data/python/rbbt/workflow.py +64 -0
- data/python/test.py +10 -0
- data/share/rbbt_commands/workflow/retry +43 -0
- data/share/rbbt_commands/workflow/server +1 -1
- data/share/rbbt_commands/workflow/task +4 -2
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b6312e564dc119b942bb279017a12bd3571f27b1caa57db746c62996e8c73407
|
4
|
+
data.tar.gz: f08802c255d5967bf211bf801fbeb7f91e9d2a82804f51393061ce422e7fc26e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f74e126c73d121853002d2918ef5cb8c9b181c7c2264d7eb7c9f4a4d78441fc6b026f1b27eca445613717fff6a8f0169f9aeb86d33afeafe2eebed696685cc3
|
7
|
+
data.tar.gz: 93ddc50c5c0f5b45e79449f81788405fada4ad9aabaeac1f6fd908765184a79220e2960472a460a973afedd31ae2ad389c60d67a80e4b82ae63ac70748bf8a25
|
data/LICENSE
CHANGED
data/bin/rbbt_exec.rb
CHANGED
@@ -30,7 +30,7 @@ data = data * "\n" if Array === data
|
|
30
30
|
|
31
31
|
case
|
32
32
|
when (output.nil? or output == '-')
|
33
|
-
|
33
|
+
STDOUT.write data
|
34
34
|
when output == "file"
|
35
35
|
if Misc.filename? data
|
36
36
|
tmpfile = data
|
@@ -39,7 +39,7 @@ when output == "file"
|
|
39
39
|
Open.write(tmpfile, data.to_s)
|
40
40
|
end
|
41
41
|
|
42
|
-
puts tmpfile
|
42
|
+
STDOUT.puts tmpfile
|
43
43
|
else
|
44
44
|
Open.write(output, data.to_s)
|
45
45
|
end
|
data/lib/rbbt/tsv/excel.rb
CHANGED
data/lib/rbbt/util/filecache.rb
CHANGED
data/lib/rbbt/util/migrate.rb
CHANGED
@@ -1,90 +1,92 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
ENV[var]
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
return
|
83
|
-
return true if
|
84
|
-
return
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
1
|
+
require_relative 'refactor'
|
2
|
+
Rbbt.require_instead 'scout/misc/format'
|
3
|
+
#module Misc
|
4
|
+
#
|
5
|
+
# def self.hostname
|
6
|
+
# @hostanem ||= `hostname`.strip
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# def self.pid_exists?(pid)
|
10
|
+
# return false if pid.nil?
|
11
|
+
# begin
|
12
|
+
# Process.getpgid(pid.to_i)
|
13
|
+
# true
|
14
|
+
# rescue Errno::ESRCH
|
15
|
+
# false
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# def self.env_add(var, value, sep = ":", prepend = true)
|
20
|
+
# ENV[var] ||= ""
|
21
|
+
# return if ENV[var] =~ /(#{sep}|^)#{Regexp.quote value}(#{sep}|$)/
|
22
|
+
# if prepend
|
23
|
+
# ENV[var] = value + sep + ENV[var]
|
24
|
+
# else
|
25
|
+
# ENV[var] += sep + ENV[var]
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# def self.with_env(var, value, &block)
|
30
|
+
# var = var.to_s
|
31
|
+
# value = value.to_s
|
32
|
+
# current = ENV[var]
|
33
|
+
# begin
|
34
|
+
# ENV[var] = value
|
35
|
+
# yield
|
36
|
+
# ensure
|
37
|
+
# ENV[var] = current
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# def self.common_path(dir, file)
|
42
|
+
# file = File.expand_path file
|
43
|
+
# dir = File.expand_path dir
|
44
|
+
#
|
45
|
+
# return true if file == dir
|
46
|
+
# while File.dirname(file) != file
|
47
|
+
# file = File.dirname(file)
|
48
|
+
# return true if file == dir
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# return false
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
#
|
55
|
+
# def self.relative_link(source, target_dir)
|
56
|
+
# path = "."
|
57
|
+
# current = target_dir
|
58
|
+
# while ! Misc.common_path current, source
|
59
|
+
# current = File.dirname(current)
|
60
|
+
# path = File.join(path, '..')
|
61
|
+
# return nil if current == "/"
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# File.join(path, Misc.path_relative_to(current, source))
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# # WARN: probably not thread safe...
|
68
|
+
# def self.in_dir(dir)
|
69
|
+
# old_pwd = FileUtils.pwd
|
70
|
+
# res = nil
|
71
|
+
# begin
|
72
|
+
# FileUtils.mkdir_p dir unless File.exist?(dir)
|
73
|
+
# FileUtils.cd dir
|
74
|
+
# res = yield
|
75
|
+
# ensure
|
76
|
+
# FileUtils.cd old_pwd
|
77
|
+
# end
|
78
|
+
# res
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# def self.is_filename?(string, need_to_exists = true)
|
82
|
+
# return false if string.nil?
|
83
|
+
# return true if defined? Path and Path === string
|
84
|
+
# return true if string.respond_to? :exists
|
85
|
+
# return true if String === string and ! string.include?("\n") and string.split("/").select{|p| p.length > 265}.empty? and (! need_to_exists || File.exist?(string))
|
86
|
+
# return false
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# class << self
|
90
|
+
# alias filename? is_filename?
|
91
|
+
# end
|
92
|
+
#end
|
@@ -1,66 +1,66 @@
|
|
1
|
-
module Workflow
|
2
|
-
|
3
|
-
annotation :asynchronous_exports, :synchronous_exports, :exec_exports, :stream_exports
|
4
|
-
|
5
|
-
def asynchronous_exports
|
6
|
-
@asynchronous_exports ||= []
|
7
|
-
end
|
8
|
-
|
9
|
-
def synchronous_exports
|
10
|
-
@synchronous_exports ||= []
|
11
|
-
end
|
12
|
-
|
13
|
-
def exec_exports
|
14
|
-
@exec_exports ||= []
|
15
|
-
end
|
16
|
-
|
17
|
-
def stream_exports
|
18
|
-
@exec_exports ||= []
|
19
|
-
end
|
20
|
-
|
21
|
-
|
22
|
-
def all_exports
|
23
|
-
asynchronous_exports + synchronous_exports + exec_exports + stream_exports
|
24
|
-
end
|
25
|
-
|
26
|
-
alias task_exports all_exports
|
27
|
-
|
28
|
-
def unexport(*names)
|
29
|
-
names = names.collect{|n| n.to_s} + names.collect{|n| n.to_sym}
|
30
|
-
names.uniq!
|
31
|
-
exec_exports.replace exec_exports - names if exec_exports
|
32
|
-
synchronous_exports.replace synchronous_exports - names if synchronous_exports
|
33
|
-
asynchronous_exports.replace asynchronous_exports - names if asynchronous_exports
|
34
|
-
stream_exports.replace stream_exports - names if stream_exports
|
35
|
-
end
|
36
|
-
|
37
|
-
def export_exec(*names)
|
38
|
-
unexport *names
|
39
|
-
exec_exports.concat names
|
40
|
-
exec_exports.uniq!
|
41
|
-
exec_exports
|
42
|
-
end
|
43
|
-
|
44
|
-
def export_synchronous(*names)
|
45
|
-
unexport *names
|
46
|
-
synchronous_exports.concat names
|
47
|
-
synchronous_exports.uniq!
|
48
|
-
synchronous_exports
|
49
|
-
end
|
50
|
-
|
51
|
-
def export_asynchronous(*names)
|
52
|
-
unexport *names
|
53
|
-
asynchronous_exports.concat names
|
54
|
-
asynchronous_exports.uniq!
|
55
|
-
asynchronous_exports
|
56
|
-
end
|
57
|
-
|
58
|
-
def export_stream(*names)
|
59
|
-
unexport *names
|
60
|
-
stream_exports.concat names
|
61
|
-
stream_exports.uniq!
|
62
|
-
stream_exports
|
63
|
-
end
|
64
|
-
|
65
|
-
alias export export_asynchronous
|
66
|
-
end
|
1
|
+
#module Workflow
|
2
|
+
#
|
3
|
+
# annotation :asynchronous_exports, :synchronous_exports, :exec_exports, :stream_exports
|
4
|
+
#
|
5
|
+
# def asynchronous_exports
|
6
|
+
# @asynchronous_exports ||= []
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# def synchronous_exports
|
10
|
+
# @synchronous_exports ||= []
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# def exec_exports
|
14
|
+
# @exec_exports ||= []
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# def stream_exports
|
18
|
+
# @exec_exports ||= []
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
#
|
22
|
+
# def all_exports
|
23
|
+
# asynchronous_exports + synchronous_exports + exec_exports + stream_exports
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# alias task_exports all_exports
|
27
|
+
#
|
28
|
+
# def unexport(*names)
|
29
|
+
# names = names.collect{|n| n.to_s} + names.collect{|n| n.to_sym}
|
30
|
+
# names.uniq!
|
31
|
+
# exec_exports.replace exec_exports - names if exec_exports
|
32
|
+
# synchronous_exports.replace synchronous_exports - names if synchronous_exports
|
33
|
+
# asynchronous_exports.replace asynchronous_exports - names if asynchronous_exports
|
34
|
+
# stream_exports.replace stream_exports - names if stream_exports
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# def export_exec(*names)
|
38
|
+
# unexport *names
|
39
|
+
# exec_exports.concat names
|
40
|
+
# exec_exports.uniq!
|
41
|
+
# exec_exports
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# def export_synchronous(*names)
|
45
|
+
# unexport *names
|
46
|
+
# synchronous_exports.concat names
|
47
|
+
# synchronous_exports.uniq!
|
48
|
+
# synchronous_exports
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# def export_asynchronous(*names)
|
52
|
+
# unexport *names
|
53
|
+
# asynchronous_exports.concat names
|
54
|
+
# asynchronous_exports.uniq!
|
55
|
+
# asynchronous_exports
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# def export_stream(*names)
|
59
|
+
# unexport *names
|
60
|
+
# stream_exports.concat names
|
61
|
+
# stream_exports.uniq!
|
62
|
+
# stream_exports
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# alias export export_asynchronous
|
66
|
+
#end
|
@@ -1,64 +1,64 @@
|
|
1
|
-
module Workflow
|
2
|
-
def rec_inputs(task_name)
|
3
|
-
tasks[task_name].recursive_inputs.collect{|name, _| name }
|
4
|
-
end
|
5
|
-
|
6
|
-
def rec_input_types(task_name)
|
7
|
-
tasks[task_name].recursive_inputs.inject({}) do |acc,l|
|
8
|
-
name, type, desc, default, options = l
|
9
|
-
acc.merge!(name => type) unless acc.include?(name)
|
10
|
-
acc
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
|
15
|
-
def rec_input_descriptions(task_name)
|
16
|
-
tasks[task_name].recursive_inputs.inject({}) do |acc,l|
|
17
|
-
name, type, desc, default, options = l
|
18
|
-
acc.merge!(name => desc) unless desc.nil? || acc.include?(name)
|
19
|
-
acc
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def rec_input_defaults(task_name)
|
24
|
-
tasks[task_name].recursive_inputs.inject({}) do |acc,l|
|
25
|
-
name, type, desc, default, options = l
|
26
|
-
acc.merge!(name => default) unless default.nil? || acc.include?(name)
|
27
|
-
acc
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def rec_input_options(task_name)
|
32
|
-
tasks[task_name].recursive_inputs.inject({}) do |acc,l|
|
33
|
-
name, type, desc, default, options = l
|
34
|
-
acc.merge!(name => options) unless options.nil? unless acc.include?(name)
|
35
|
-
acc
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
|
40
|
-
def rec_input_use(task_name)
|
41
|
-
input_use = {}
|
42
|
-
task = self.tasks[task_name]
|
43
|
-
task.inputs.each do |name,_|
|
44
|
-
input_use[name] ||= {}
|
45
|
-
input_use[name][self] ||= []
|
46
|
-
input_use[name][self] << task_name
|
47
|
-
end
|
48
|
-
|
49
|
-
task.deps.inject(input_use) do |acc,p|
|
50
|
-
workflow, task_name = p
|
51
|
-
next if task_name.nil?
|
52
|
-
workflow.rec_input_use(task_name).each do |name,uses|
|
53
|
-
acc[name] ||= {}
|
54
|
-
uses.each do |workflow, task_names|
|
55
|
-
acc[name][workflow] ||= []
|
56
|
-
acc[name][workflow].concat(task_names)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
acc
|
60
|
-
end if task.deps
|
61
|
-
|
62
|
-
input_use
|
63
|
-
end
|
64
|
-
end
|
1
|
+
#module Workflow
|
2
|
+
# def rec_inputs(task_name)
|
3
|
+
# tasks[task_name].recursive_inputs.collect{|name, _| name }
|
4
|
+
# end
|
5
|
+
#
|
6
|
+
# def rec_input_types(task_name)
|
7
|
+
# tasks[task_name].recursive_inputs.inject({}) do |acc,l|
|
8
|
+
# name, type, desc, default, options = l
|
9
|
+
# acc.merge!(name => type) unless acc.include?(name)
|
10
|
+
# acc
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
#
|
15
|
+
# def rec_input_descriptions(task_name)
|
16
|
+
# tasks[task_name].recursive_inputs.inject({}) do |acc,l|
|
17
|
+
# name, type, desc, default, options = l
|
18
|
+
# acc.merge!(name => desc) unless desc.nil? || acc.include?(name)
|
19
|
+
# acc
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# def rec_input_defaults(task_name)
|
24
|
+
# tasks[task_name].recursive_inputs.inject({}) do |acc,l|
|
25
|
+
# name, type, desc, default, options = l
|
26
|
+
# acc.merge!(name => default) unless default.nil? || acc.include?(name)
|
27
|
+
# acc
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# def rec_input_options(task_name)
|
32
|
+
# tasks[task_name].recursive_inputs.inject({}) do |acc,l|
|
33
|
+
# name, type, desc, default, options = l
|
34
|
+
# acc.merge!(name => options) unless options.nil? unless acc.include?(name)
|
35
|
+
# acc
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
#
|
40
|
+
# def rec_input_use(task_name)
|
41
|
+
# input_use = {}
|
42
|
+
# task = self.tasks[task_name]
|
43
|
+
# task.inputs.each do |name,_|
|
44
|
+
# input_use[name] ||= {}
|
45
|
+
# input_use[name][self] ||= []
|
46
|
+
# input_use[name][self] << task_name
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# task.deps.inject(input_use) do |acc,p|
|
50
|
+
# workflow, task_name = p
|
51
|
+
# next if task_name.nil?
|
52
|
+
# workflow.rec_input_use(task_name).each do |name,uses|
|
53
|
+
# acc[name] ||= {}
|
54
|
+
# uses.each do |workflow, task_names|
|
55
|
+
# acc[name][workflow] ||= []
|
56
|
+
# acc[name][workflow].concat(task_names)
|
57
|
+
# end
|
58
|
+
# end
|
59
|
+
# acc
|
60
|
+
# end if task.deps
|
61
|
+
#
|
62
|
+
# input_use
|
63
|
+
# end
|
64
|
+
#end
|
@@ -1,65 +1,66 @@
|
|
1
|
-
require_relative 'export'
|
2
|
-
require_relative 'recursive'
|
3
|
-
module Workflow
|
4
|
-
def task_info(name)
|
5
|
-
name = name.to_sym
|
6
|
-
task = tasks[name]
|
7
|
-
raise "No '#{name}' task in '#{self.to_s}' Workflow" if task.nil?
|
8
|
-
id = File.join(self.to_s, name.to_s)
|
9
|
-
@task_info ||= {}
|
10
|
-
@task_info[id] ||= begin
|
11
|
-
description = task.description
|
12
|
-
result_description = task.result_description
|
13
|
-
|
14
|
-
|
15
|
-
inputs = rec_inputs(name).uniq
|
16
|
-
input_types = rec_input_types(name)
|
17
|
-
input_descriptions = rec_input_descriptions(name)
|
18
|
-
input_use = rec_input_use(name)
|
19
|
-
input_defaults = rec_input_defaults(name)
|
20
|
-
input_options = rec_input_options(name)
|
21
|
-
extension = task.extension
|
22
|
-
export = case
|
23
|
-
when (synchronous_exports.include?(name.to_sym) or synchronous_exports.include?(name.to_s))
|
24
|
-
:synchronous
|
25
|
-
when (asynchronous_exports.include?(name.to_sym) or asynchronous_exports.include?(name.to_s))
|
26
|
-
:asynchronous
|
27
|
-
when (exec_exports.include?(name.to_sym) or exec_exports.include?(name.to_s))
|
28
|
-
:exec
|
29
|
-
when (stream_exports.include?(name.to_sym) or stream_exports.include?(name.to_s))
|
30
|
-
:stream
|
31
|
-
else
|
32
|
-
:none
|
33
|
-
end
|
34
|
-
|
35
|
-
dependencies = tasks[name].deps
|
36
|
-
{ :id => id,
|
37
|
-
:description => description,
|
38
|
-
:export => export,
|
39
|
-
:inputs => inputs,
|
40
|
-
:input_types => input_types,
|
41
|
-
:input_descriptions => input_descriptions,
|
42
|
-
:input_defaults => input_defaults,
|
43
|
-
:input_options => input_options,
|
44
|
-
:input_use => input_use,
|
45
|
-
:
|
46
|
-
|
47
|
-
|
48
|
-
:
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
1
|
+
#require_relative 'export'
|
2
|
+
#require_relative 'recursive'
|
3
|
+
#module Workflow
|
4
|
+
# def task_info(name)
|
5
|
+
# name = name.to_sym
|
6
|
+
# task = tasks[name]
|
7
|
+
# raise "No '#{name}' task in '#{self.to_s}' Workflow" if task.nil?
|
8
|
+
# id = File.join(self.to_s, name.to_s)
|
9
|
+
# @task_info ||= {}
|
10
|
+
# @task_info[id] ||= begin
|
11
|
+
# description = task.description
|
12
|
+
# result_description = task.result_description
|
13
|
+
# returns = task.returns
|
14
|
+
#
|
15
|
+
# inputs = rec_inputs(name).uniq
|
16
|
+
# input_types = rec_input_types(name)
|
17
|
+
# input_descriptions = rec_input_descriptions(name)
|
18
|
+
# input_use = rec_input_use(name)
|
19
|
+
# input_defaults = rec_input_defaults(name)
|
20
|
+
# input_options = rec_input_options(name)
|
21
|
+
# extension = task.extension
|
22
|
+
# export = case
|
23
|
+
# when (synchronous_exports.include?(name.to_sym) or synchronous_exports.include?(name.to_s))
|
24
|
+
# :synchronous
|
25
|
+
# when (asynchronous_exports.include?(name.to_sym) or asynchronous_exports.include?(name.to_s))
|
26
|
+
# :asynchronous
|
27
|
+
# when (exec_exports.include?(name.to_sym) or exec_exports.include?(name.to_s))
|
28
|
+
# :exec
|
29
|
+
# when (stream_exports.include?(name.to_sym) or stream_exports.include?(name.to_s))
|
30
|
+
# :stream
|
31
|
+
# else
|
32
|
+
# :none
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# dependencies = tasks[name].deps
|
36
|
+
# { :id => id,
|
37
|
+
# :description => description,
|
38
|
+
# :export => export,
|
39
|
+
# :inputs => inputs,
|
40
|
+
# :input_types => input_types,
|
41
|
+
# :input_descriptions => input_descriptions,
|
42
|
+
# :input_defaults => input_defaults,
|
43
|
+
# :input_options => input_options,
|
44
|
+
# :input_use => input_use,
|
45
|
+
# :returns => returns,
|
46
|
+
# #:result_type => result_type,
|
47
|
+
# #:result_description => result_description,
|
48
|
+
# :dependencies => dependencies,
|
49
|
+
# :extension => extension
|
50
|
+
# }
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#end
|
54
|
+
#
|
55
|
+
#module Task
|
56
|
+
# def result_description
|
57
|
+
# ""
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# def result_type
|
61
|
+
# @returns
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
#end
|
65
|
+
#
|
66
|
+
#
|
@@ -146,7 +146,6 @@ class RemoteWorkflow
|
|
146
146
|
task_info = RemoteWorkflow::REST.get_json(File.join(url, task.to_s, 'info'))
|
147
147
|
task_info = RemoteWorkflow.fix_hash(task_info)
|
148
148
|
|
149
|
-
task_info[:result_type] = task_info[:result_type].to_sym
|
150
149
|
task_info[:export] = task_info[:export].to_sym
|
151
150
|
task_info[:input_types] = RemoteWorkflow.fix_hash(task_info[:input_types], true)
|
152
151
|
task_info[:inputs] = task_info[:inputs].collect{|input| input.to_sym }
|
@@ -248,7 +247,6 @@ class RemoteWorkflow
|
|
248
247
|
end
|
249
248
|
end
|
250
249
|
|
251
|
-
|
252
250
|
def task_info(task)
|
253
251
|
RemoteWorkflow::REST.task_info(url, task)
|
254
252
|
end
|
@@ -260,6 +258,7 @@ class RemoteWorkflow
|
|
260
258
|
@exec_exports = (task_exports["exec"] || []).collect{|task| task.to_sym }
|
261
259
|
@stream_exports = (task_exports["stream"] || []).collect{|task| task.to_sym }
|
262
260
|
@can_stream = task_exports["can_stream"]
|
261
|
+
(@asynchronous_exports + @synchronous_exports + @exec_exports).uniq.each do |e| tasks[e] end
|
263
262
|
end
|
264
263
|
end
|
265
264
|
end
|
data/lib/rbbt-util.rb
CHANGED
@@ -20,7 +20,7 @@ require_relative 'rbbt/tsv'
|
|
20
20
|
require_relative 'rbbt/workflow'
|
21
21
|
|
22
22
|
Persist.cache_dir = Rbbt.var.cache.persistence
|
23
|
-
FileCache.cachedir = Rbbt.var.cache.filecache.find
|
24
|
-
TmpFile.tmpdir = Rbbt.tmp.find
|
23
|
+
FileCache.cachedir = Rbbt.var.cache.filecache.find
|
24
|
+
TmpFile.tmpdir = Rbbt.tmp.find
|
25
25
|
Resource.default_resource = Rbbt
|
26
26
|
|
data/python/rbbt/__init__.py
CHANGED
@@ -1,22 +1,29 @@
|
|
1
|
-
import warnings
|
2
1
|
import sys
|
3
2
|
import os
|
4
3
|
import subprocess
|
4
|
+
import tempfile
|
5
|
+
import shutil
|
6
|
+
import pandas
|
7
|
+
import numpy
|
5
8
|
|
6
|
-
|
9
|
+
|
10
|
+
def cmd(cmd=None):
|
7
11
|
if cmd is None:
|
8
12
|
print("Rbbt")
|
9
13
|
else:
|
10
14
|
return subprocess.run('rbbt_exec.rb', input=cmd.encode('utf-8'), capture_output=True).stdout.decode()
|
11
15
|
|
16
|
+
|
12
17
|
def libdir():
|
13
|
-
return
|
18
|
+
return cmd('puts Rbbt.find(:lib)').rstrip()
|
19
|
+
|
14
20
|
|
15
21
|
def add_libdir():
|
16
22
|
pythondir = os.path.join(libdir(), 'python')
|
17
23
|
sys.path.insert(0, pythondir)
|
18
24
|
|
19
|
-
|
25
|
+
|
26
|
+
def path(subdir=None, base_dir=None):
|
20
27
|
from pathlib import Path
|
21
28
|
import os
|
22
29
|
|
@@ -144,4 +151,71 @@ def save_tsv(filename, df, key=None):
|
|
144
151
|
key = "#" + key
|
145
152
|
df.to_csv(filename, sep="\t", index_label=key)
|
146
153
|
|
154
|
+
def save_job_inputs(data):
|
155
|
+
temp_dir = tempfile.mkdtemp() # Create a temporary directory
|
156
|
+
|
157
|
+
for name, value in data.items():
|
158
|
+
file_path = os.path.join(temp_dir, name)
|
159
|
+
|
160
|
+
if isinstance(value, str):
|
161
|
+
file_path += ".txt"
|
162
|
+
with open(file_path, "w") as f:
|
163
|
+
f.write(value)
|
164
|
+
|
165
|
+
elif isinstance(value, (bool)):
|
166
|
+
with open(file_path, "w") as f:
|
167
|
+
if value:
|
168
|
+
f.write('true')
|
169
|
+
else:
|
170
|
+
f.write('false')
|
171
|
+
|
172
|
+
elif isinstance(value, (int, float)):
|
173
|
+
with open(file_path, "w") as f:
|
174
|
+
f.write(str(value))
|
175
|
+
|
176
|
+
elif isinstance(value, pandas.DataFrame):
|
177
|
+
file_path += ".tsv"
|
178
|
+
save_tsv(file_path, value)
|
147
179
|
|
180
|
+
elif isinstance(value, numpy.ndarray) or isinstance(value, list):
|
181
|
+
file_path += ".list"
|
182
|
+
with open(file_path, "w") as f:
|
183
|
+
f.write("\n".join(value))
|
184
|
+
|
185
|
+
else:
|
186
|
+
raise TypeError(f"Unsupported data type for argument '{name}': {type(value)}")
|
187
|
+
|
188
|
+
return temp_dir
|
189
|
+
|
190
|
+
|
191
|
+
def run_job(workflow, task, name='Default', fork=False, clean=False, **kwargs):
|
192
|
+
inputs_dir = save_job_inputs(kwargs)
|
193
|
+
cmd = ['rbbt', 'workflow', 'task', workflow, task, '--jobname', name, '--load_inputs', inputs_dir, '--nocolor']
|
194
|
+
|
195
|
+
if fork:
|
196
|
+
cmd.append('--fork')
|
197
|
+
cmd.append('--detach')
|
198
|
+
|
199
|
+
if clean:
|
200
|
+
if clean == 'recursive':
|
201
|
+
cmd.append('--recursive_clean')
|
202
|
+
else:
|
203
|
+
cmd.append('--clean')
|
204
|
+
|
205
|
+
proc = subprocess.run(
|
206
|
+
cmd,
|
207
|
+
capture_output=True, # Capture both stdout and stderr
|
208
|
+
text=True # Automatically decode outputs to strings
|
209
|
+
)
|
210
|
+
shutil.rmtree(inputs_dir)
|
211
|
+
if proc.returncode != 0:
|
212
|
+
output = proc.stderr.strip()
|
213
|
+
if output == '' :
|
214
|
+
output = proc.stdout.strip()
|
215
|
+
raise RuntimeError(output) # Raise error with cleaned stderr content
|
216
|
+
return proc.stdout.strip()
|
217
|
+
|
218
|
+
if __name__ == "__main__":
|
219
|
+
import json
|
220
|
+
res = run_job('Baking', 'bake_muffin_tray', 'test', add_blueberries=True, fork=True)
|
221
|
+
print(res)
|
@@ -0,0 +1,104 @@
|
|
1
|
+
import requests
|
2
|
+
import logging
|
3
|
+
import json
|
4
|
+
from urllib.parse import urlencode, urljoin
|
5
|
+
from time import sleep
|
6
|
+
import itertools
|
7
|
+
|
8
|
+
def request_post(url, params):
|
9
|
+
response = requests.post(url, params)
|
10
|
+
return response
|
11
|
+
|
12
|
+
def request_get(url, params):
|
13
|
+
query = urlencode(params)
|
14
|
+
full_url = f"{url}?{query}"
|
15
|
+
response = requests.get(full_url)
|
16
|
+
return response
|
17
|
+
|
18
|
+
def get_json(url, params={}):
|
19
|
+
params['_format'] = 'json'
|
20
|
+
response = request_get(url, params)
|
21
|
+
if response.status_code == 200:
|
22
|
+
return json.loads(response.content) # parse the JSON content from the response
|
23
|
+
else:
|
24
|
+
logging.error("Failed to initialize remote tasks")
|
25
|
+
|
26
|
+
def get_raw(url, params={}):
|
27
|
+
params['_format'] = 'raw'
|
28
|
+
response = request_get(url, params)
|
29
|
+
if response.status_code == 200:
|
30
|
+
return response.content # parse the JSON content from the response
|
31
|
+
else:
|
32
|
+
logging.error("Failed to initialize remote tasks")
|
33
|
+
|
34
|
+
def join(url, *subpaths):
|
35
|
+
return url + "/" + "/".join(subpaths)
|
36
|
+
|
37
|
+
class RemoteStep:
|
38
|
+
def __init__(self, url):
|
39
|
+
self.url = url
|
40
|
+
|
41
|
+
def info(self):
|
42
|
+
return get_json(join(self.url, 'info'))
|
43
|
+
def status(self):
|
44
|
+
return self.info()['status']
|
45
|
+
|
46
|
+
def done(self):
|
47
|
+
return self.status() == 'done'
|
48
|
+
|
49
|
+
def error(self):
|
50
|
+
return self.status() == 'error' or self.status() == 'aborted'
|
51
|
+
|
52
|
+
def running(self):
|
53
|
+
return not (self.done() or self.error())
|
54
|
+
|
55
|
+
def wait(self, time=1):
|
56
|
+
while self.running():
|
57
|
+
sleep(time)
|
58
|
+
|
59
|
+
|
60
|
+
def raw(self):
|
61
|
+
return get_raw(self.url)
|
62
|
+
|
63
|
+
def json(self):
|
64
|
+
return get_json(self.url)
|
65
|
+
|
66
|
+
class RemoteWorkflow:
|
67
|
+
def __init__(self, url):
|
68
|
+
self.url = url
|
69
|
+
self.task_exports = {}
|
70
|
+
self.init_remote_tasks()
|
71
|
+
|
72
|
+
def init_remote_tasks(self):
|
73
|
+
self.task_exports = get_json(self.url)
|
74
|
+
self.tasks = []
|
75
|
+
self.tasks += self.task_exports['asynchronous']
|
76
|
+
self.tasks += self.task_exports['synchronous']
|
77
|
+
self.tasks += self.task_exports['exec']
|
78
|
+
|
79
|
+
def task_info(self, name):
|
80
|
+
return get_json(join(self.url, name, '/info'))
|
81
|
+
|
82
|
+
def job(self, task, **kwargs):
|
83
|
+
kwargs['_format'] = 'jobname'
|
84
|
+
response = request_post(join(self.url, task), kwargs)
|
85
|
+
if response.status_code == 200:
|
86
|
+
jobname = response.content.decode('utf-8')
|
87
|
+
step_url = join(self.url, task, jobname)
|
88
|
+
print(step_url)
|
89
|
+
return RemoteStep(step_url)
|
90
|
+
else:
|
91
|
+
logging.error("Failed to initialize remote tasks")
|
92
|
+
|
93
|
+
|
94
|
+
if __name__ == "__main__":
|
95
|
+
wf = RemoteWorkflow('http://localhost:1900/Baking')
|
96
|
+
print(wf.tasks)
|
97
|
+
print(wf.task_info('bake_muffin_tray'))
|
98
|
+
|
99
|
+
step = wf.job('bake_muffin_tray', add_blueberries=True)
|
100
|
+
step.wait()
|
101
|
+
print(step.json())
|
102
|
+
|
103
|
+
|
104
|
+
|
@@ -0,0 +1,64 @@
|
|
1
|
+
from . import cmd, run_job
|
2
|
+
import subprocess
|
3
|
+
import json
|
4
|
+
import time
|
5
|
+
|
6
|
+
def save_inputs(directory, inputs, types):
|
7
|
+
return
|
8
|
+
|
9
|
+
class Workflow:
|
10
|
+
def __init__(self, name):
|
11
|
+
self.name = name
|
12
|
+
|
13
|
+
def tasks(self):
|
14
|
+
ruby=f'Workflow.require_workflow("{self.name}").tasks.keys * "\n"'
|
15
|
+
return cmd(ruby).strip().split("\n")
|
16
|
+
|
17
|
+
def task_info(self, name):
|
18
|
+
ruby=f'Workflow.require_workflow("{self.name}").task_info("{name}").to_json'
|
19
|
+
return cmd(ruby)
|
20
|
+
|
21
|
+
def run(self, task, **kwargs):
|
22
|
+
return run_job(self.name, task, **kwargs)
|
23
|
+
|
24
|
+
def fork(self, task, **kwargs):
|
25
|
+
path = run_job(self.name, task, fork=True, **kwargs)
|
26
|
+
return Step(path)
|
27
|
+
|
28
|
+
class Step:
|
29
|
+
def __init__(self, path):
|
30
|
+
self.path = path
|
31
|
+
self.info_content = None
|
32
|
+
|
33
|
+
def info(self):
|
34
|
+
if self.info_content:
|
35
|
+
return self.info_content
|
36
|
+
ruby=f'puts Step.load("{self.path}").info.to_json'
|
37
|
+
txt = cmd(ruby)
|
38
|
+
info_content = json.loads(txt)
|
39
|
+
status = info_content["status"]
|
40
|
+
if status == "done" or status == "error" or status == "aborted":
|
41
|
+
self.info_content = info_content
|
42
|
+
return info_content
|
43
|
+
|
44
|
+
def status(self):
|
45
|
+
return self.info()["status"]
|
46
|
+
|
47
|
+
def done(self):
|
48
|
+
return self.status() == 'done'
|
49
|
+
|
50
|
+
def error(self):
|
51
|
+
return self.status() == 'error'
|
52
|
+
|
53
|
+
def aborted(self):
|
54
|
+
return self.status() == 'aborted'
|
55
|
+
|
56
|
+
def join(self):
|
57
|
+
while not (self.done() or self.error() or self.aborted()):
|
58
|
+
time.sleep(1)
|
59
|
+
|
60
|
+
def load(self):
|
61
|
+
ruby=f'puts Step.load("{self.path}").load.to_json'
|
62
|
+
txt = cmd(ruby)
|
63
|
+
return json.loads(txt)
|
64
|
+
|
data/python/test.py
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rbbt/workflow'
|
4
|
+
|
5
|
+
require 'rbbt-util'
|
6
|
+
require 'rbbt-util'
|
7
|
+
require 'rbbt/util/simpleopt'
|
8
|
+
|
9
|
+
$0 = "rbbt #{$previous_commands*""} #{ File.basename(__FILE__) }" if $previous_commands
|
10
|
+
|
11
|
+
options = SOPT.setup <<EOF
|
12
|
+
Retry a failed job
|
13
|
+
|
14
|
+
$ rbbt workflow retry <job-path>
|
15
|
+
|
16
|
+
Does not retry if job is done unless clean is specified
|
17
|
+
|
18
|
+
-h--help Help
|
19
|
+
-cl--clean Clean the job
|
20
|
+
-rcl--recursive_clean Recursively clean the job
|
21
|
+
EOF
|
22
|
+
|
23
|
+
SOPT.usage if options[:help]
|
24
|
+
|
25
|
+
file = ARGV.shift
|
26
|
+
def get_step(file)
|
27
|
+
file = file.sub(/\.(info|files)/,'')
|
28
|
+
step = Workflow.load_step file
|
29
|
+
step
|
30
|
+
end
|
31
|
+
|
32
|
+
step = get_step(file)
|
33
|
+
inputs = step.recursive_inputs
|
34
|
+
|
35
|
+
step.clean if options[:clean]
|
36
|
+
step.recursive_clean if options[:recursive_clean]
|
37
|
+
|
38
|
+
if ! step.done?
|
39
|
+
wf = Workflow.require_workflow step.workflow
|
40
|
+
job = wf.job(step.task_name, step.clean_name, inputs.to_hash)
|
41
|
+
job.run
|
42
|
+
end
|
43
|
+
|
@@ -535,12 +535,14 @@ begin
|
|
535
535
|
end
|
536
536
|
end
|
537
537
|
|
538
|
-
if do_fork
|
538
|
+
if do_fork || detach
|
539
539
|
ENV["SCOUT_NO_PROGRESS"] = "true"
|
540
540
|
if detach
|
541
541
|
job.fork
|
542
542
|
Process.detach job.pid if job.pid
|
543
|
-
|
543
|
+
Log.info(Log.color(:magenta, "Issued: ") + Log.color(:magenta, job.pid ? job.pid.to_s : 'no pid') + ' -- ' + job.path)
|
544
|
+
puts job.path
|
545
|
+
|
544
546
|
exit 0
|
545
547
|
end
|
546
548
|
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbbt-util
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.0.
|
4
|
+
version: 6.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miguel Vazquez
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date: 2025-
|
10
|
+
date: 2025-03-31 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: scout-gear
|
@@ -376,6 +375,9 @@ files:
|
|
376
375
|
- lib/rbbt/workflow/util/provenance.rb
|
377
376
|
- lib/rbbt/workflow/util/trace.rb
|
378
377
|
- python/rbbt/__init__.py
|
378
|
+
- python/rbbt/workflow.py
|
379
|
+
- python/rbbt/workflow/remote.py
|
380
|
+
- python/test.py
|
379
381
|
- share/Rlib/plot.R
|
380
382
|
- share/Rlib/svg.R
|
381
383
|
- share/Rlib/util.R
|
@@ -488,6 +490,7 @@ files:
|
|
488
490
|
- share/rbbt_commands/workflow/remote/add
|
489
491
|
- share/rbbt_commands/workflow/remote/list
|
490
492
|
- share/rbbt_commands/workflow/remote/remove
|
493
|
+
- share/rbbt_commands/workflow/retry
|
491
494
|
- share/rbbt_commands/workflow/server
|
492
495
|
- share/rbbt_commands/workflow/task
|
493
496
|
- share/rbbt_commands/workflow/trace
|
@@ -500,7 +503,6 @@ homepage: http://github.com/mikisvaz/rbbt-util
|
|
500
503
|
licenses:
|
501
504
|
- MIT
|
502
505
|
metadata: {}
|
503
|
-
post_install_message:
|
504
506
|
rdoc_options: []
|
505
507
|
require_paths:
|
506
508
|
- lib
|
@@ -515,8 +517,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
515
517
|
- !ruby/object:Gem::Version
|
516
518
|
version: '0'
|
517
519
|
requirements: []
|
518
|
-
rubygems_version: 3.5
|
519
|
-
signing_key:
|
520
|
+
rubygems_version: 3.6.5
|
520
521
|
specification_version: 4
|
521
522
|
summary: Utilities for the Ruby Bioinformatics Toolkit (rbbt)
|
522
523
|
test_files: []
|