thor 0.13.7 → 0.13.8
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.
- data/CHANGELOG.rdoc +1 -0
- data/lib/thor.rb +9 -5
- data/lib/thor/base.rb +2 -1
- data/lib/thor/group.rb +1 -1
- data/lib/thor/invocation.rb +1 -1
- data/lib/thor/parser/argument.rb +15 -15
- data/lib/thor/parser/option.rb +38 -46
- data/lib/thor/parser/options.rb +1 -1
- data/lib/thor/task.rb +52 -40
- data/lib/thor/version.rb +1 -1
- data/spec/fixtures/script.thor +6 -0
- data/spec/parser/option_spec.rb +2 -2
- data/spec/spec_helper.rb +3 -4
- data/spec/task_spec.rb +5 -5
- data/spec/thor_spec.rb +32 -4
- metadata +4 -4
data/CHANGELOG.rdoc
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
== 0.13, released 2010-02-03
|
2
2
|
|
3
|
+
* Added :lazy_default which is only triggered if a switch is given
|
3
4
|
* Added Thor::Shell::HTML
|
4
5
|
* Decoupled Thor::Group and Thor, so it's easier to vendor
|
5
6
|
* Added check_unknown_options! in case you want error messages to be raised in valid switches
|
data/lib/thor.rb
CHANGED
@@ -23,6 +23,7 @@ class Thor
|
|
23
23
|
# ==== Parameters
|
24
24
|
# usage<String>
|
25
25
|
# description<String>
|
26
|
+
# options<String>
|
26
27
|
#
|
27
28
|
def desc(usage, description, options={})
|
28
29
|
if options[:for]
|
@@ -30,7 +31,7 @@ class Thor
|
|
30
31
|
task.usage = usage if usage
|
31
32
|
task.description = description if description
|
32
33
|
else
|
33
|
-
@usage, @desc = usage, description
|
34
|
+
@usage, @desc, @hide = usage, description, options[:hide] || false
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|
@@ -135,6 +136,7 @@ class Thor
|
|
135
136
|
#
|
136
137
|
def start(original_args=ARGV, config={})
|
137
138
|
@@original_args = original_args
|
139
|
+
|
138
140
|
super do |given_args|
|
139
141
|
meth = given_args.first.to_s
|
140
142
|
|
@@ -154,7 +156,7 @@ class Thor
|
|
154
156
|
args, opts = given_args, {}
|
155
157
|
end
|
156
158
|
|
157
|
-
task ||= Thor::
|
159
|
+
task ||= Thor::DynamicTask.new(meth)
|
158
160
|
trailing = args[Range.new(arguments.size, -1)]
|
159
161
|
new(args, opts, config).invoke(task, trailing || [])
|
160
162
|
end
|
@@ -204,11 +206,12 @@ class Thor
|
|
204
206
|
# Returns tasks ready to be printed.
|
205
207
|
def printable_tasks(all = true, subcommand = false)
|
206
208
|
(all ? all_tasks : tasks).map do |_, task|
|
209
|
+
next if task.hidden?
|
207
210
|
item = []
|
208
211
|
item << banner(task, false, subcommand)
|
209
212
|
item << (task.description ? "# #{task.description.gsub(/\s+/m,' ')}" : "")
|
210
213
|
item
|
211
|
-
end
|
214
|
+
end.compact
|
212
215
|
end
|
213
216
|
|
214
217
|
def subcommands
|
@@ -239,8 +242,9 @@ class Thor
|
|
239
242
|
|
240
243
|
def create_task(meth) #:nodoc:
|
241
244
|
if @usage && @desc
|
242
|
-
|
243
|
-
|
245
|
+
base_class = @hide ? Thor::HiddenTask : Thor::Task
|
246
|
+
tasks[meth.to_s] = base_class.new(meth, @desc, @long_desc, @usage, method_options)
|
247
|
+
@usage, @desc, @long_desc, @method_options, @hide = nil
|
244
248
|
true
|
245
249
|
elsif self.all_tasks[meth.to_s] || meth.to_sym == :method_missing
|
246
250
|
true
|
data/lib/thor/base.rb
CHANGED
@@ -336,6 +336,7 @@ class Thor
|
|
336
336
|
def no_tasks
|
337
337
|
@no_tasks = true
|
338
338
|
yield
|
339
|
+
ensure
|
339
340
|
@no_tasks = false
|
340
341
|
end
|
341
342
|
|
@@ -449,7 +450,7 @@ class Thor
|
|
449
450
|
def build_option(name, options, scope) #:nodoc:
|
450
451
|
scope[name] = Thor::Option.new(name, options[:desc], options[:required],
|
451
452
|
options[:type], options[:default], options[:banner],
|
452
|
-
options[:group], options[:aliases])
|
453
|
+
options[:lazy_default], options[:group], options[:aliases])
|
453
454
|
end
|
454
455
|
|
455
456
|
# Receives a hash of options, parse them and add to the scope. This is a
|
data/lib/thor/group.rb
CHANGED
data/lib/thor/invocation.rb
CHANGED
@@ -156,7 +156,7 @@ class Thor
|
|
156
156
|
raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base
|
157
157
|
|
158
158
|
task ||= klass.default_task if klass.respond_to?(:default_task)
|
159
|
-
task = klass.all_tasks[task.to_s] || Thor::
|
159
|
+
task = klass.all_tasks[task.to_s] || Thor::DynamicTask.new(task) if task && !task.is_a?(Thor::Task)
|
160
160
|
task
|
161
161
|
end
|
162
162
|
|
data/lib/thor/parser/argument.rb
CHANGED
@@ -31,10 +31,10 @@ class Thor
|
|
31
31
|
|
32
32
|
def show_default?
|
33
33
|
case default
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
when Array, String, Hash
|
35
|
+
!default.empty?
|
36
|
+
else
|
37
|
+
default
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -45,21 +45,21 @@ class Thor
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def valid_type?(type)
|
48
|
-
VALID_TYPES.include?(type.to_sym)
|
48
|
+
self.class::VALID_TYPES.include?(type.to_sym)
|
49
49
|
end
|
50
50
|
|
51
51
|
def default_banner
|
52
52
|
case type
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
53
|
+
when :boolean
|
54
|
+
nil
|
55
|
+
when :string, :default
|
56
|
+
human_name.upcase
|
57
|
+
when :numeric
|
58
|
+
"N"
|
59
|
+
when :hash
|
60
|
+
"key:value"
|
61
|
+
when :array
|
62
|
+
"one two three"
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
data/lib/thor/parser/option.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
class Thor
|
2
2
|
class Option < Argument #:nodoc:
|
3
|
-
attr_reader :aliases, :group
|
3
|
+
attr_reader :aliases, :group, :lazy_default
|
4
4
|
|
5
5
|
VALID_TYPES = [:boolean, :numeric, :hash, :array, :string]
|
6
6
|
|
7
|
-
def initialize(name, description=nil, required=nil, type=nil, default=nil, banner=nil, group=nil, aliases=nil)
|
7
|
+
def initialize(name, description=nil, required=nil, type=nil, default=nil, banner=nil, lazy_default=nil, group=nil, aliases=nil)
|
8
8
|
super(name, description, required, type, default, banner)
|
9
|
-
@
|
10
|
-
@group
|
9
|
+
@lazy_default = lazy_default
|
10
|
+
@group = group.to_s.capitalize if group
|
11
|
+
@aliases = [*aliases].compact
|
11
12
|
end
|
12
13
|
|
13
14
|
# This parse quick options given as method_options. It makes several
|
@@ -48,23 +49,22 @@ class Thor
|
|
48
49
|
default = value
|
49
50
|
|
50
51
|
type = case value
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
value.class.name.downcase.to_sym
|
52
|
+
when Symbol
|
53
|
+
default = nil
|
54
|
+
if VALID_TYPES.include?(value)
|
55
|
+
value
|
56
|
+
elsif required = (value == :required)
|
57
|
+
:string
|
58
|
+
end
|
59
|
+
when TrueClass, FalseClass
|
60
|
+
:boolean
|
61
|
+
when Numeric
|
62
|
+
:numeric
|
63
|
+
when Hash, Array, String
|
64
|
+
value.class.name.downcase.to_sym
|
65
65
|
end
|
66
66
|
|
67
|
-
self.new(name.to_s, nil, required, type, default, nil, nil, aliases)
|
67
|
+
self.new(name.to_s, nil, required, type, default, nil, nil, nil, aliases)
|
68
68
|
end
|
69
69
|
|
70
70
|
def switch_name
|
@@ -91,38 +91,30 @@ class Thor
|
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
else
|
101
|
-
super
|
102
|
-
end
|
94
|
+
VALID_TYPES.each do |type|
|
95
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
96
|
+
def #{type}?
|
97
|
+
self.type == #{type.inspect}
|
98
|
+
end
|
99
|
+
RUBY
|
103
100
|
end
|
104
101
|
|
105
|
-
|
106
|
-
|
107
|
-
def validate!
|
108
|
-
raise ArgumentError, "An option cannot be boolean and required." if boolean? && required?
|
109
|
-
end
|
110
|
-
|
111
|
-
def valid_type?(type)
|
112
|
-
VALID_TYPES.include?(type.to_sym)
|
113
|
-
end
|
102
|
+
protected
|
114
103
|
|
115
|
-
|
116
|
-
|
117
|
-
|
104
|
+
def validate!
|
105
|
+
raise ArgumentError, "An option cannot be boolean and required." if boolean? && required?
|
106
|
+
end
|
118
107
|
|
119
|
-
|
120
|
-
|
121
|
-
|
108
|
+
def dasherized?
|
109
|
+
name.index('-') == 0
|
110
|
+
end
|
122
111
|
|
123
|
-
|
124
|
-
|
125
|
-
|
112
|
+
def undasherize(str)
|
113
|
+
str.sub(/^-{1,2}/, '')
|
114
|
+
end
|
126
115
|
|
116
|
+
def dasherize(str)
|
117
|
+
(str.length > 1 ? "--" : "-") + str.gsub('_', '-')
|
118
|
+
end
|
127
119
|
end
|
128
120
|
end
|
data/lib/thor/parser/options.rb
CHANGED
@@ -150,7 +150,7 @@ class Thor
|
|
150
150
|
return nil # User set value to nil
|
151
151
|
elsif option.string? && !option.required?
|
152
152
|
# Return the default if there is one, else the human name
|
153
|
-
return option.default || option.human_name
|
153
|
+
return option.lazy_default || option.default || option.human_name
|
154
154
|
else
|
155
155
|
raise MalformattedArgumentError, "No value provided for option '#{switch}'"
|
156
156
|
end
|
data/lib/thor/task.rb
CHANGED
@@ -2,21 +2,6 @@ class Thor
|
|
2
2
|
class Task < Struct.new(:name, :description, :long_description, :usage, :options)
|
3
3
|
FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/
|
4
4
|
|
5
|
-
# A dynamic task that handles method missing scenarios.
|
6
|
-
class Dynamic < Task
|
7
|
-
def initialize(name, options=nil)
|
8
|
-
super(name.to_s, "A dynamically-generated task", name.to_s, name.to_s, options)
|
9
|
-
end
|
10
|
-
|
11
|
-
def run(instance, args=[])
|
12
|
-
if (instance.methods & [name.to_s, name.to_sym]).empty?
|
13
|
-
super
|
14
|
-
else
|
15
|
-
instance.class.handle_no_task_error(name)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
5
|
def initialize(name, description, long_description, usage, options=nil)
|
21
6
|
super(name.to_s, description, long_description, usage, options || {})
|
22
7
|
end
|
@@ -26,6 +11,10 @@ class Thor
|
|
26
11
|
self.options = other.options.dup if other.options
|
27
12
|
end
|
28
13
|
|
14
|
+
def hidden?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
29
18
|
# By default, a task invokes a method in the thor class. You can change this
|
30
19
|
# implementation to create custom tasks.
|
31
20
|
def run(instance, args=[])
|
@@ -42,9 +31,8 @@ class Thor
|
|
42
31
|
# Returns the formatted usage by injecting given required arguments
|
43
32
|
# and required options into the given usage.
|
44
33
|
def formatted_usage(klass, namespace = true, subcommand = false)
|
45
|
-
namespace = klass.namespace unless namespace == false
|
46
|
-
|
47
34
|
if namespace
|
35
|
+
namespace = klass.namespace
|
48
36
|
formatted = "#{namespace.gsub(/^(default)/,'')}:"
|
49
37
|
formatted.sub!(/.$/, ' ') if subcommand
|
50
38
|
end
|
@@ -67,36 +55,60 @@ class Thor
|
|
67
55
|
formatted.strip
|
68
56
|
end
|
69
57
|
|
70
|
-
|
58
|
+
protected
|
71
59
|
|
72
|
-
|
73
|
-
|
74
|
-
|
60
|
+
def not_debugging?(instance)
|
61
|
+
!(instance.class.respond_to?(:debugging) && instance.class.debugging)
|
62
|
+
end
|
75
63
|
|
76
|
-
|
77
|
-
|
78
|
-
|
64
|
+
def required_options
|
65
|
+
@required_options ||= options.map{ |_, o| o.usage if o.required? }.compact.sort.join(" ")
|
66
|
+
end
|
79
67
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
68
|
+
# Given a target, checks if this class name is not a private/protected method.
|
69
|
+
def public_method?(instance) #:nodoc:
|
70
|
+
collection = instance.private_methods + instance.protected_methods
|
71
|
+
(collection & [name.to_s, name.to_sym]).empty?
|
72
|
+
end
|
85
73
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
74
|
+
def sans_backtrace(backtrace, caller) #:nodoc:
|
75
|
+
saned = backtrace.reject { |frame| frame =~ FILE_REGEXP }
|
76
|
+
saned -= caller
|
77
|
+
end
|
90
78
|
|
91
|
-
|
92
|
-
|
93
|
-
|
79
|
+
def handle_argument_error?(instance, error, caller)
|
80
|
+
not_debugging?(instance) && error.message =~ /wrong number of arguments/ && begin
|
81
|
+
saned = sans_backtrace(error.backtrace, caller)
|
82
|
+
# Ruby 1.9 always include the called method in the backtrace
|
83
|
+
saned.empty? || (saned.size == 1 && RUBY_VERSION >= "1.9")
|
94
84
|
end
|
85
|
+
end
|
95
86
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
87
|
+
def handle_no_method_error?(instance, error, caller)
|
88
|
+
not_debugging?(instance) &&
|
89
|
+
error.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/
|
90
|
+
end
|
91
|
+
end
|
100
92
|
|
93
|
+
# A task that is hidden in help messages but still invocable.
|
94
|
+
class HiddenTask < Task
|
95
|
+
def hidden?
|
96
|
+
true
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# A dynamic task that handles method missing scenarios.
|
101
|
+
class DynamicTask < Task
|
102
|
+
def initialize(name, options=nil)
|
103
|
+
super(name.to_s, "A dynamically-generated task", name.to_s, name.to_s, options)
|
104
|
+
end
|
105
|
+
|
106
|
+
def run(instance, args=[])
|
107
|
+
if (instance.methods & [name.to_s, name.to_sym]).empty?
|
108
|
+
super
|
109
|
+
else
|
110
|
+
instance.class.handle_no_task_error(name)
|
111
|
+
end
|
112
|
+
end
|
101
113
|
end
|
102
114
|
end
|
data/lib/thor/version.rb
CHANGED
data/spec/fixtures/script.thor
CHANGED
@@ -26,6 +26,11 @@ class MyScript < Thor
|
|
26
26
|
[type]
|
27
27
|
end
|
28
28
|
|
29
|
+
desc "hidden TYPE", "this is hidden", :hide => true
|
30
|
+
def hidden(type)
|
31
|
+
[type]
|
32
|
+
end
|
33
|
+
|
29
34
|
desc "foo BAR", <<END
|
30
35
|
do some fooing
|
31
36
|
This is more info!
|
@@ -67,6 +72,7 @@ END
|
|
67
72
|
end
|
68
73
|
|
69
74
|
method_options :all => :boolean
|
75
|
+
method_option :lazy, :lazy_default => "yes"
|
70
76
|
desc "with_optional NAME", "invoke with optional name"
|
71
77
|
def with_optional(name=nil)
|
72
78
|
[ name, options ]
|
data/spec/parser/option_spec.rb
CHANGED
@@ -6,8 +6,8 @@ describe Thor::Option do
|
|
6
6
|
Thor::Option.parse(key, value)
|
7
7
|
end
|
8
8
|
|
9
|
-
def option(name,
|
10
|
-
@option ||= Thor::Option.new(name,
|
9
|
+
def option(name, *args)
|
10
|
+
@option ||= Thor::Option.new(name, *args)
|
11
11
|
end
|
12
12
|
|
13
13
|
describe "#parse" do
|
data/spec/spec_helper.rb
CHANGED
@@ -10,7 +10,10 @@ require 'rdoc'
|
|
10
10
|
require 'diff/lcs' # You need diff/lcs installed to run specs (but not to run Thor).
|
11
11
|
require 'fakeweb' # You need fakeweb installed to run specs (but not to run Thor).
|
12
12
|
|
13
|
+
# Set shell to basic
|
14
|
+
$0 = "thor"
|
13
15
|
$thor_runner = true
|
16
|
+
Thor::Base.shell = Thor::Shell::Basic
|
14
17
|
|
15
18
|
# Load fixtures
|
16
19
|
load File.join(File.dirname(__FILE__), "fixtures", "task.thor")
|
@@ -18,10 +21,6 @@ load File.join(File.dirname(__FILE__), "fixtures", "group.thor")
|
|
18
21
|
load File.join(File.dirname(__FILE__), "fixtures", "script.thor")
|
19
22
|
load File.join(File.dirname(__FILE__), "fixtures", "invoke.thor")
|
20
23
|
|
21
|
-
# Set shell to basic
|
22
|
-
$0 = "thor"
|
23
|
-
Thor::Base.shell = Thor::Shell::Basic
|
24
|
-
|
25
24
|
Kernel.module_eval do
|
26
25
|
alias_method :must, :should
|
27
26
|
alias_method :must_not, :should_not
|
data/spec/task_spec.rb
CHANGED
@@ -31,16 +31,16 @@ describe Thor::Task do
|
|
31
31
|
|
32
32
|
describe "#dynamic" do
|
33
33
|
it "creates a dynamic task with the given name" do
|
34
|
-
Thor::
|
35
|
-
Thor::
|
36
|
-
Thor::
|
37
|
-
Thor::
|
34
|
+
Thor::DynamicTask.new('task').name.must == 'task'
|
35
|
+
Thor::DynamicTask.new('task').description.must == 'A dynamically-generated task'
|
36
|
+
Thor::DynamicTask.new('task').usage.must == 'task'
|
37
|
+
Thor::DynamicTask.new('task').options.must == {}
|
38
38
|
end
|
39
39
|
|
40
40
|
it "does not invoke an existing method" do
|
41
41
|
mock = mock()
|
42
42
|
mock.class.should_receive(:handle_no_task_error).with("to_s")
|
43
|
-
Thor::
|
43
|
+
Thor::DynamicTask.new('to_s').run(mock)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
data/spec/thor_spec.rb
CHANGED
@@ -8,6 +8,17 @@ describe Thor do
|
|
8
8
|
options.must == { "force" => true }
|
9
9
|
end
|
10
10
|
|
11
|
+
it "sets method_option with given parameters" do
|
12
|
+
arg, options = MyScript.start(["with_optional"])
|
13
|
+
options.must == {}
|
14
|
+
|
15
|
+
arg, options = MyScript.start(["with_optional", "--lazy"])
|
16
|
+
options.must == { "lazy" => "yes" }
|
17
|
+
|
18
|
+
arg, options = MyScript.start(["with_optional", "--lazy", "yesyes!"])
|
19
|
+
options.must == { "lazy" => "yesyes!" }
|
20
|
+
end
|
21
|
+
|
11
22
|
describe "when :for is supplied" do
|
12
23
|
it "updates an already defined task" do
|
13
24
|
args, options = MyChildScript.start(["animal", "horse", "--other=fish"])
|
@@ -66,12 +77,19 @@ describe Thor do
|
|
66
77
|
end
|
67
78
|
|
68
79
|
describe "#desc" do
|
69
|
-
|
70
|
-
|
80
|
+
it "provides description for a task" do
|
81
|
+
content = capture(:stdout) { MyScript.start(["help"]) }
|
82
|
+
content.must =~ /thor my_script:zoo\s+# zoo around/m
|
71
83
|
end
|
72
84
|
|
73
|
-
it "provides
|
74
|
-
|
85
|
+
it "provides no namespace if $thor_runner is false" do
|
86
|
+
begin
|
87
|
+
$thor_runner = false
|
88
|
+
content = capture(:stdout) { MyScript.start(["help"]) }
|
89
|
+
content.must =~ /thor zoo\s+# zoo around/m
|
90
|
+
ensure
|
91
|
+
$thor_runner = true
|
92
|
+
end
|
75
93
|
end
|
76
94
|
|
77
95
|
describe "when :for is supplied" do
|
@@ -79,6 +97,16 @@ describe Thor do
|
|
79
97
|
capture(:stdout) { MyChildScript.start(["help"]) }.must =~ /animal KIND \s+# fish around/m
|
80
98
|
end
|
81
99
|
end
|
100
|
+
|
101
|
+
describe "when :hide is supplied" do
|
102
|
+
it "does not show the task in help" do
|
103
|
+
capture(:stdout) { MyScript.start(["help"]) }.must_not =~ /this is hidden/m
|
104
|
+
end
|
105
|
+
|
106
|
+
it "but the task is still invokcable not show the task in help" do
|
107
|
+
MyScript.start(["hidden", "yesyes"]).must == ["yesyes"]
|
108
|
+
end
|
109
|
+
end
|
82
110
|
end
|
83
111
|
|
84
112
|
describe "#method_options" do
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 59
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 13
|
9
|
-
-
|
10
|
-
version: 0.13.
|
9
|
+
- 8
|
10
|
+
version: 0.13.8
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Yehuda Katz
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2010-
|
19
|
+
date: 2010-07-16 00:00:00 +02:00
|
20
20
|
default_executable:
|
21
21
|
dependencies: []
|
22
22
|
|