class Puppet::Util::CommandLine::Trollop::Parser

The commandline parser. In typical usage, the methods in this class will be handled internally by Trollop::options. In this case, only the opt, banner and version, depends, and conflicts methods will typically be called.

If you want to instantiate this class yourself (for more complicated argument-parsing logic), call parse to actually produce the output hash, and consider calling it from within Trollop::with_standard_exception_handling.

Constants

FLAG_TYPES

The set of values that indicate a flag option when passed as the :type parameter of opt.

MULTI_ARG_TYPES

The set of values that indicate a multiple-parameter option (i.e., that takes multiple space-separated values on the commandline) when passed as the :type parameter of opt.

SINGLE_ARG_TYPES

The set of values that indicate a single-parameter (normal) option when passed as the :type parameter of opt.

A value of io corresponds to a readable IO resource, including a filename, URI, or the strings 'stdin' or '-'.

TYPES

The complete set of legal values for the :type parameter of opt.

Attributes

create_default_short_options[RW]

A flag that determines whether or not to attempt to automatically generate “short” options if they are not

explicitly specified.
handle_help_and_version[RW]

A flag indicating whether or not the parser should attempt to handle “–help” and

"--version" specially.  If 'false', it will treat them just like any other option.
ignore_invalid_options[RW]

A flag that determines whether or not to raise an error if the parser is passed one or more

options that were not registered ahead of time.  If 'true', then the parser will simply
ignore options that it does not recognize.
leftovers[R]

The values from the commandline that were not interpreted by parse.

specs[R]

The complete configuration hashes for each option. (Mainly useful for testing.)

Public Class Methods

new(*a, &b) click to toggle source

Initializes the parser, and instance-evaluates any block given.

    # File lib/puppet/util/command_line/trollop.rb
 94 def initialize *a, &b
 95   @version = nil
 96   @leftovers = []
 97   @specs = {}
 98   @long = {}
 99   @short = {}
100   @order = []
101   @constraints = []
102   @stop_words = []
103   @stop_on_unknown = false
104 
105   #instance_eval(&b) if b # can't take arguments
106   cloaker(&b).bind(self).call(*a) if b
107 end

Public Instance Methods

banner(s;) click to toggle source

Adds text to the help display. Can be interspersed with calls to opt to build a multi-section help page.

Also aliased as: text
conflicts(*syms) click to toggle source

Marks two (or more!) options as conflicting.

    # File lib/puppet/util/command_line/trollop.rb
279 def conflicts *syms
280   syms.each { |sym| raise ArgumentError, _("unknown option '%{sym}'") % { sym: sym } unless @specs[sym] }
281   @constraints << [:conflicts, syms]
282 end
depends(*syms) click to toggle source

Marks two (or more!) options as requiring each other. Only handles undirected (i.e., mutual) dependencies. Directed dependencies are better modeled with Trollop::die.

    # File lib/puppet/util/command_line/trollop.rb
273 def depends *syms
274   syms.each { |sym| raise ArgumentError, _("unknown option '%{sym}'") % { sym: sym } unless @specs[sym] }
275   @constraints << [:depends, syms]
276 end
die(arg, msg) click to toggle source

The per-parser version of Trollop::die (see that for documentation).

    # File lib/puppet/util/command_line/trollop.rb
553 def die arg, msg
554   if msg
555     $stderr.puts _("Error: argument --%{value0} %{msg}.") % { value0: @specs[arg][:long], msg: msg }
556   else
557     $stderr.puts _("Error: %{arg}.") % { arg: arg }
558   end
559   $stderr.puts _("Try --help for help.")
560   exit(-1)
561 end
educate(stream=$stdout) click to toggle source

Print the help message to stream.

    # File lib/puppet/util/command_line/trollop.rb
464 def educate stream=$stdout
465   width # just calculate it now; otherwise we have to be careful not to
466         # call this unless the cursor's at the beginning of a line.
467 
468   left = {}
469   @specs.each do |name, spec|
470     left[name] = "--#{spec[:long]}" +
471       (spec[:short] && spec[:short] != :none ? ", -#{spec[:short]}" : "") +
472       case spec[:type]
473       when :flag; ""
474       when :int; " <i>"
475       when :ints; " <i+>"
476       when :string; " <s>"
477       when :strings; " <s+>"
478       when :float; " <f>"
479       when :floats; " <f+>"
480       when :io; " <filename/uri>"
481       when :ios; " <filename/uri+>"
482       when :date; " <date>"
483       when :dates; " <date+>"
484       end
485   end
486 
487   leftcol_width = left.values.map { |s| s.length }.max || 0
488   rightcol_start = leftcol_width + 6 # spaces
489 
490   unless @order.size > 0 && @order.first.first == :text
491     stream.puts "#@version\n" if @version
492     stream.puts _("Options:")
493   end
494 
495   @order.each do |what, opt|
496     if what == :text
497       stream.puts wrap(opt)
498       next
499     end
500 
501     spec = @specs[opt]
502     stream.printf "  %#{leftcol_width}s:   ", left[opt]
503     desc = spec[:desc] + begin
504       default_s = case spec[:default]
505       when $stdout; "<stdout>"
506       when $stdin; "<stdin>"
507       when $stderr; "<stderr>"
508       when Array
509         spec[:default].join(", ")
510       else
511         spec[:default].to_s
512       end
513 
514       if spec[:default]
515         if spec[:desc] =~ /\.$/
516           _(" (Default: %{default_s})") % { default_s: default_s }
517         else
518           _(" (default: %{default_s})") % { default_s: default_s }
519         end
520       else
521         ""
522       end
523     end
524     stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)
525   end
526 end
opt(name, desc="", opts={}) click to toggle source

Define an option. name is the option name, a unique identifier for the option that you will use internally, which should be a symbol or a string. desc is a string description which will be displayed in help messages.

Takes the following optional arguments:

:long

Specify the long form of the argument, i.e. the form with two dashes. If unspecified, will be automatically derived based on the argument name by turning the name option into a string, and replacing any _'s by -'s.

:short

Specify the short form of the argument, i.e. the form with one dash. If unspecified, will be automatically derived from name.

:type

Require that the argument take a parameter or parameters of type type. For a single parameter, the value can be a member of SINGLE_ARG_TYPES, or a corresponding Ruby class (e.g. Integer for :int). For multiple-argument parameters, the value can be any member of MULTI_ARG_TYPES constant. If unset, the default argument type is :flag, meaning that the argument does not take a parameter. The specification of :type is not necessary if a :default is given.

:default

Set the default value for an argument. Without a default value, the hash returned by parse (and thus Trollop::options) will have a nil value for this key unless the argument is given on the commandline. The argument type is derived automatically from the class of the default value given, so specifying a :type is not necessary if a :default is given. (But see below for an important caveat when :multi: is specified too.) If the argument is a flag, and the default is set to true, then if it is specified on the commandline the value will be false.

:required

If set to true, the argument must be provided on the commandline.

:multi

If set to true, allows multiple occurrences of the option on the commandline. Otherwise, only a single instance of the option is allowed. (Note that this is different from taking multiple parameters. See below.)

Note that there are two types of argument multiplicity: an argument can take multiple values, e.g. “–arg 1 2 3”. An argument can also be allowed to occur multiple times, e.g. “–arg 1 –arg 2”.

Arguments that take multiple values should have a :type parameter drawn from MULTI_ARG_TYPES (e.g. :strings), or a :default: value of an array of the correct type (e.g. [String]). The value of this argument will be an array of the parameters on the commandline.

Arguments that can occur multiple times should be marked with :multi => true. The value of this argument will also be an array. In contrast with regular non-multi options, if not specified on the commandline, the default value will be [], not nil.

These two attributes can be combined (e.g. :type => :strings, :multi => true), in which case the value of the argument will be an array of arrays.

There's one ambiguous case to be aware of: when :multi: is true and a :default is set to an array (of something), it's ambiguous whether this is a multi-value argument as well as a multi-occurrence argument. In this case, Trollop assumes that it's not a multi-value argument. If you want a multi-value, multi-occurrence argument with a default value, you must specify :type as well.

    # File lib/puppet/util/command_line/trollop.rb
149 def opt name, desc="", opts={}
150   raise ArgumentError, _("you already have an argument named '%{name}'") % { name: name } if @specs.member? name
151 
152   ## fill in :type
153   opts[:type] = # normalize
154     case opts[:type]
155     when :boolean, :bool; :flag
156     when :integer; :int
157     when :integers; :ints
158     when :double; :float
159     when :doubles; :floats
160     when Class
161       case opts[:type].name
162       when 'TrueClass', 'FalseClass'; :flag
163       when 'String'; :string
164       when 'Integer'; :int
165       when 'Float'; :float
166       when 'IO'; :io
167       when 'Date'; :date
168       else
169         raise ArgumentError, _("unsupported argument type '%{type}'") % { type: opts[:type].class.name }
170       end
171     when nil; nil
172     else
173       raise ArgumentError, _("unsupported argument type '%{type}'") % { type: opts[:type] } unless TYPES.include?(opts[:type])
174       opts[:type]
175     end
176 
177   ## for options with :multi => true, an array default doesn't imply
178   ## a multi-valued argument. for that you have to specify a :type
179   ## as well. (this is how we disambiguate an ambiguous situation;
180   ## see the docs for Parser#opt for details.)
181   disambiguated_default =
182     if opts[:multi] && opts[:default].is_a?(Array) && !opts[:type]
183       opts[:default].first
184     else
185       opts[:default]
186     end
187 
188   type_from_default =
189     case disambiguated_default
190     when Integer; :int
191     when Numeric; :float
192     when TrueClass, FalseClass; :flag
193     when String; :string
194     when IO; :io
195     when Date; :date
196     when Array
197       if opts[:default].empty?
198         raise ArgumentError, _("multiple argument type cannot be deduced from an empty array for '%{value0}'") % { value0: opts[:default][0].class.name }
199       end
200       case opts[:default][0]    # the first element determines the types
201       when Integer; :ints
202       when Numeric; :floats
203       when String; :strings
204       when IO; :ios
205       when Date; :dates
206       else
207         raise ArgumentError, _("unsupported multiple argument type '%{value0}'") % { value0: opts[:default][0].class.name }
208       end
209     when nil; nil
210     else
211       raise ArgumentError, _("unsupported argument type '%{value0}'") % { value0: opts[:default].class.name }
212     end
213 
214   raise ArgumentError, _(":type specification and default type don't match (default type is %{type_from_default})") % { type_from_default: type_from_default } if opts[:type] && type_from_default && opts[:type] != type_from_default
215 
216   opts[:type] = opts[:type] || type_from_default || :flag
217 
218   ## fill in :long
219   opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.tr("_", "-")
220   opts[:long] =
221     case opts[:long]
222     when /^--([^-].*)$/
223       $1
224     when /^[^-]/
225       opts[:long]
226     else
227       raise ArgumentError, _("invalid long option name %{name}") % { name: opts[:long].inspect }
228     end
229   raise ArgumentError, _("long option name %{value0} is already taken; please specify a (different) :long") % { value0: opts[:long].inspect } if @long[opts[:long]]
230 
231   ## fill in :short
232   opts[:short] = opts[:short].to_s if opts[:short] unless opts[:short] == :none
233   opts[:short] = case opts[:short]
234     when /^-(.)$/; $1
235     when nil, :none, /^.$/; opts[:short]
236     else raise ArgumentError, _("invalid short option name '%{name}'") % { name: opts[:short].inspect }
237   end
238 
239   if opts[:short]
240     raise ArgumentError, _("short option name %{value0} is already taken; please specify a (different) :short") % { value0: opts[:short].inspect } if @short[opts[:short]]
241     raise ArgumentError, _("a short option name can't be a number or a dash") if opts[:short] =~ INVALID_SHORT_ARG_REGEX
242   end
243 
244   ## fill in :default for flags
245   opts[:default] = false if opts[:type] == :flag && opts[:default].nil?
246 
247   ## autobox :default for :multi (multi-occurrence) arguments
248   opts[:default] = [opts[:default]] if opts[:default] && opts[:multi] && !opts[:default].is_a?(Array)
249 
250   ## fill in :multi
251   opts[:multi] ||= false
252 
253   opts[:desc] ||= desc
254   @long[opts[:long]] = name
255   @short[opts[:short]] = name if opts[:short] && opts[:short] != :none
256   @specs[name] = opts
257   @order << [:opt, name]
258 end
parse(cmdline=ARGV) click to toggle source

Parses the commandline. Typically called by Trollop::options, but you can call it directly if you need more control.

throws CommandlineError, HelpNeeded, and VersionNeeded exceptions.

    # File lib/puppet/util/command_line/trollop.rb
309 def parse cmdline=ARGV
310   vals = {}
311   required = {}
312 
313   if handle_help_and_version
314     opt :version, _("Print version and exit") if @version unless @specs[:version] || @long["version"]
315     opt :help, _("Show this message") unless @specs[:help] || @long["help"]
316   end
317 
318   @specs.each do |sym, opts|
319     required[sym] = true if opts[:required]
320     vals[sym] = opts[:default]
321     vals[sym] = [] if opts[:multi] && !opts[:default] # multi arguments default to [], not nil
322   end
323 
324   resolve_default_short_options if create_default_short_options
325 
326   ## resolve symbols
327   given_args = {}
328   @leftovers = each_arg cmdline do |arg, params|
329     sym = case arg
330     when /^-([^-])$/
331       @short[$1]
332     when /^--no-([^-]\S*)$/
333       @long["[no-]#{$1}"]
334     when /^--([^-]\S*)$/
335       @long[$1] ? @long[$1] : @long["[no-]#{$1}"]
336     else
337       raise CommandlineError, _("invalid argument syntax: '%{arg}'") % { arg: arg }
338     end
339 
340     unless sym
341       next 0 if ignore_invalid_options
342       raise CommandlineError, _("unknown argument '%{arg}'") % { arg: arg } unless sym
343     end
344 
345     if given_args.include?(sym) && !@specs[sym][:multi]
346       raise CommandlineError, _("option '%{arg}' specified multiple times") % { arg: arg }
347     end
348 
349     given_args[sym] ||= {}
350 
351     given_args[sym][:arg] = arg
352     given_args[sym][:params] ||= []
353 
354     # The block returns the number of parameters taken.
355     num_params_taken = 0
356 
357     unless params.nil?
358       if SINGLE_ARG_TYPES.include?(@specs[sym][:type])
359         given_args[sym][:params] << params[0, 1]  # take the first parameter
360         num_params_taken = 1
361       elsif MULTI_ARG_TYPES.include?(@specs[sym][:type])
362         given_args[sym][:params] << params        # take all the parameters
363         num_params_taken = params.size
364       end
365     end
366 
367     num_params_taken
368   end
369 
370   if handle_help_and_version
371     ## check for version and help args
372     raise VersionNeeded if given_args.include? :version
373     raise HelpNeeded if given_args.include? :help
374   end
375 
376   ## check constraint satisfaction
377   @constraints.each do |type, syms|
378     constraint_sym = syms.find { |sym| given_args[sym] }
379     next unless constraint_sym
380 
381     case type
382     when :depends
383       syms.each { |sym| raise CommandlineError, _("--%{value0} requires --%{value1}") % { value0: @specs[constraint_sym][:long], value1: @specs[sym][:long] } unless given_args.include? sym }
384     when :conflicts
385       syms.each { |sym| raise CommandlineError, _("--%{value0} conflicts with --%{value1}") % { value0: @specs[constraint_sym][:long], value1: @specs[sym][:long] } if given_args.include?(sym) && (sym != constraint_sym) }
386     end
387   end
388 
389   required.each do |sym, val|
390     raise CommandlineError, _("option --%{opt} must be specified") % { opt: @specs[sym][:long] } unless given_args.include? sym
391   end
392 
393   ## parse parameters
394   given_args.each do |sym, given_data|
395     arg = given_data[:arg]
396     params = given_data[:params]
397 
398     opts = @specs[sym]
399     raise CommandlineError, _("option '%{arg}' needs a parameter") % { arg: arg } if params.empty? && opts[:type] != :flag
400 
401     vals["#{sym}_given".intern] = true # mark argument as specified on the commandline
402 
403     case opts[:type]
404     when :flag
405       if arg =~ /^--no-/ and sym.to_s =~ /^--\[no-\]/
406         vals[sym] = opts[:default]
407       else
408         vals[sym] = !opts[:default]
409       end
410     when :int, :ints
411       vals[sym] = params.map { |pg| pg.map { |p| parse_integer_parameter p, arg } }
412     when :float, :floats
413       vals[sym] = params.map { |pg| pg.map { |p| parse_float_parameter p, arg } }
414     when :string, :strings
415       vals[sym] = params.map { |pg| pg.map { |p| p.to_s } }
416     when :io, :ios
417       vals[sym] = params.map { |pg| pg.map { |p| parse_io_parameter p, arg } }
418     when :date, :dates
419       vals[sym] = params.map { |pg| pg.map { |p| parse_date_parameter p, arg } }
420     end
421 
422     if SINGLE_ARG_TYPES.include?(opts[:type])
423       unless opts[:multi]       # single parameter
424         vals[sym] = vals[sym][0][0]
425       else                      # multiple options, each with a single parameter
426         vals[sym] = vals[sym].map { |p| p[0] }
427       end
428     elsif MULTI_ARG_TYPES.include?(opts[:type]) && !opts[:multi]
429       vals[sym] = vals[sym][0]  # single option, with multiple parameters
430     end
431     # else: multiple options, with multiple parameters
432 
433     opts[:callback].call(vals[sym]) if opts.has_key?(:callback)
434   end
435 
436   ## modify input in place with only those
437   ## arguments we didn't process
438   cmdline.clear
439   @leftovers.each { |l| cmdline << l }
440 
441   ## allow openstruct-style accessors
442   class << vals
443     def method_missing(m, *args)
444       self[m] || self[m.to_s]
445     end
446   end
447   vals
448 end
stop_on(*words) click to toggle source

Defines a set of words which cause parsing to terminate when encountered, such that any options to the left of the word are parsed as usual, and options to the right of the word are left intact.

A typical use case would be for subcommand support, where these would be set to the list of subcommands. A subsequent Trollop invocation would then be used to parse subcommand options, after shifting the subcommand off of ARGV.

    # File lib/puppet/util/command_line/trollop.rb
293 def stop_on *words
294   @stop_words = [*words].flatten
295 end
stop_on_unknown() click to toggle source

Similar to stop_on, but stops on any unknown word when encountered (unless it is a parameter for an argument). This is useful for cases where you don't know the set of subcommands ahead of time, i.e., without first parsing the global options.

    # File lib/puppet/util/command_line/trollop.rb
301 def stop_on_unknown
302   @stop_on_unknown = true
303 end
text(s;)
Alias for: banner
version(s=nil;) click to toggle source

Sets the version string. If set, the user can request the version on the commandline. Should probably be of the form “<program name> <version number>”.

    # File lib/puppet/util/command_line/trollop.rb
263 def version s=nil; @version = s if s; @version end

Private Instance Methods

cloaker(&b) click to toggle source

instance_eval but with ability to handle block arguments thanks to why: redhanded.hobix.com/inspect/aBlockCostume.html

    # File lib/puppet/util/command_line/trollop.rb
706 def cloaker &b
707   (class << self; self; end).class_eval do
708     define_method :cloaker_, &b
709     meth = instance_method :cloaker_
710     remove_method :cloaker_
711     meth
712   end
713 end
collect_argument_parameters(args, start_at) click to toggle source
    # File lib/puppet/util/command_line/trollop.rb
660 def collect_argument_parameters args, start_at
661   params = []
662   pos = start_at
663   while args[pos] && args[pos] !~ PARAM_RE && !@stop_words.member?(args[pos]) do
664     params << args[pos]
665     pos += 1
666   end
667   params
668 end
each_arg(args) { |"--#{$1}", [$2]| ... } click to toggle source

yield successive arg, parameter pairs

    # File lib/puppet/util/command_line/trollop.rb
566 def each_arg args
567   remains = []
568   i = 0
569 
570   until i >= args.length
571     if @stop_words.member? args[i]
572       remains += args[i .. -1]
573       return remains
574     end
575     case args[i]
576     when /^--$/ # arg terminator
577       remains += args[(i + 1) .. -1]
578       return remains
579     when /^--(\S+?)=(.*)$/ # long argument with equals
580       yield "--#{$1}", [$2]
581       i += 1
582     when /^--(\S+)$/ # long argument
583       params = collect_argument_parameters(args, i + 1)
584       unless params.empty?
585         num_params_taken = yield args[i], params
586         unless num_params_taken
587           if @stop_on_unknown
588             remains += args[i + 1 .. -1]
589             return remains
590           else
591             remains += params
592           end
593         end
594         i += 1 + num_params_taken
595       else # long argument no parameter
596         yield args[i], nil
597         i += 1
598       end
599     when /^-(\S+)$/ # one or more short arguments
600       shortargs = $1.split(//)
601       shortargs.each_with_index do |a, j|
602         if j == (shortargs.length - 1)
603           params = collect_argument_parameters(args, i + 1)
604           unless params.empty?
605             num_params_taken = yield "-#{a}", params
606             unless num_params_taken
607               if @stop_on_unknown
608                 remains += args[i + 1 .. -1]
609                 return remains
610               else
611                 remains += params
612               end
613             end
614             i += 1 + num_params_taken
615           else # argument no parameter
616             yield "-#{a}", nil
617             i += 1
618           end
619         else
620           yield "-#{a}", nil
621         end
622       end
623     else
624       if @stop_on_unknown
625         remains += args[i .. -1]
626         return remains
627       else
628         remains << args[i]
629         i += 1
630       end
631     end
632   end
633 
634   remains
635 end
parse_float_parameter(param, arg) click to toggle source
    # File lib/puppet/util/command_line/trollop.rb
642 def parse_float_parameter param, arg
643   raise CommandlineError, _("option '%{arg}' needs a floating-point number") % { arg: arg } unless param =~ FLOAT_RE
644   param.to_f
645 end
parse_integer_parameter(param, arg) click to toggle source
    # File lib/puppet/util/command_line/trollop.rb
637 def parse_integer_parameter param, arg
638   raise CommandlineError, _("option '%{arg}' needs an integer") % { arg: arg } unless param =~ /^\d+$/
639   param.to_i
640 end
parse_io_parameter(param, arg) click to toggle source
    # File lib/puppet/util/command_line/trollop.rb
647 def parse_io_parameter param, arg
648   case param
649   when /^(stdin|-)$/i; $stdin
650   else
651     require 'open-uri'
652     begin
653       open param
654     rescue SystemCallError => e
655       raise CommandlineError, _("file or url for option '%{arg}' cannot be opened: %{value0}") % { arg: arg, value0: e.message }, e.backtrace
656     end
657   end
658 end
resolve_default_short_options() click to toggle source
    # File lib/puppet/util/command_line/trollop.rb
670 def resolve_default_short_options
671   @order.each do |type, name|
672     next unless type == :opt
673     opts = @specs[name]
674     next if opts[:short]
675 
676     c = opts[:long].split(//).find { |d| d !~ INVALID_SHORT_ARG_REGEX && !@short.member?(d) }
677     if c # found a character to use
678       opts[:short] = c
679       @short[c] = name
680     end
681   end
682 end
wrap_line(str, opts={}) click to toggle source
    # File lib/puppet/util/command_line/trollop.rb
684 def wrap_line str, opts={}
685   prefix = opts[:prefix] || 0
686   width = opts[:width] || (self.width - 1)
687   start = 0
688   ret = []
689   until start > str.length
690     nextt =
691       if start + width >= str.length
692         str.length
693       else
694         x = str.rindex(/\s/, start + width)
695         x = str.index(/\s/, start) if x && x < start
696         x || str.length
697       end
698     ret << (ret.empty? ? "" : " " * prefix) + str[start ... nextt]
699     start = nextt + 1
700   end
701   ret
702 end