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
 93 def initialize *a, &b
 94   @version = nil
 95   @leftovers = []
 96   @specs = {}
 97   @long = {}
 98   @short = {}
 99   @order = []
100   @constraints = []
101   @stop_words = []
102   @stop_on_unknown = false
103 
104   #instance_eval(&b) if b # can't take arguments
105   cloaker(&b).bind(self).call(*a) if b
106 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
278 def conflicts *syms
279   syms.each { |sym| raise ArgumentError, _("unknown option '%{sym}'") % { sym: sym } unless @specs[sym] }
280   @constraints << [:conflicts, syms]
281 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
272 def depends *syms
273   syms.each { |sym| raise ArgumentError, _("unknown option '%{sym}'") % { sym: sym } unless @specs[sym] }
274   @constraints << [:depends, syms]
275 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
552 def die arg, msg
553   if msg
554     $stderr.puts _("Error: argument --%{value0} %{msg}.") % { value0: @specs[arg][:long], msg: msg }
555   else
556     $stderr.puts _("Error: %{arg}.") % { arg: arg }
557   end
558   $stderr.puts _("Try --help for help.")
559   exit(-1)
560 end
educate(stream=$stdout) click to toggle source

Print the help message to stream.

    # File lib/puppet/util/command_line/trollop.rb
463 def educate stream=$stdout
464   width # just calculate it now; otherwise we have to be careful not to
465         # call this unless the cursor's at the beginning of a line.
466 
467   left = {}
468   @specs.each do |name, spec|
469     left[name] = "--#{spec[:long]}" +
470       (spec[:short] && spec[:short] != :none ? ", -#{spec[:short]}" : "") +
471       case spec[:type]
472       when :flag; ""
473       when :int; " <i>"
474       when :ints; " <i+>"
475       when :string; " <s>"
476       when :strings; " <s+>"
477       when :float; " <f>"
478       when :floats; " <f+>"
479       when :io; " <filename/uri>"
480       when :ios; " <filename/uri+>"
481       when :date; " <date>"
482       when :dates; " <date+>"
483       end
484   end
485 
486   leftcol_width = left.values.map { |s| s.length }.max || 0
487   rightcol_start = leftcol_width + 6 # spaces
488 
489   unless @order.size > 0 && @order.first.first == :text
490     stream.puts "#@version\n" if @version
491     stream.puts _("Options:")
492   end
493 
494   @order.each do |what, opt|
495     if what == :text
496       stream.puts wrap(opt)
497       next
498     end
499 
500     spec = @specs[opt]
501     stream.printf "  %#{leftcol_width}s:   ", left[opt]
502     desc = spec[:desc] + begin
503       default_s = case spec[:default]
504       when $stdout; "<stdout>"
505       when $stdin; "<stdin>"
506       when $stderr; "<stderr>"
507       when Array
508         spec[:default].join(", ")
509       else
510         spec[:default].to_s
511       end
512 
513       if spec[:default]
514         if spec[:desc] =~ /\.$/
515           _(" (Default: %{default_s})") % { default_s: default_s }
516         else
517           _(" (default: %{default_s})") % { default_s: default_s }
518         end
519       else
520         ""
521       end
522     end
523     stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)
524   end
525 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
148 def opt name, desc="", opts={}
149   raise ArgumentError, _("you already have an argument named '%{name}'") % { name: name } if @specs.member? name
150 
151   ## fill in :type
152   opts[:type] = # normalize
153     case opts[:type]
154     when :boolean, :bool; :flag
155     when :integer; :int
156     when :integers; :ints
157     when :double; :float
158     when :doubles; :floats
159     when Class
160       case opts[:type].name
161       when 'TrueClass', 'FalseClass'; :flag
162       when 'String'; :string
163       when 'Integer'; :int
164       when 'Float'; :float
165       when 'IO'; :io
166       when 'Date'; :date
167       else
168         raise ArgumentError, _("unsupported argument type '%{type}'") % { type: opts[:type].class.name }
169       end
170     when nil; nil
171     else
172       raise ArgumentError, _("unsupported argument type '%{type}'") % { type: opts[:type] } unless TYPES.include?(opts[:type])
173       opts[:type]
174     end
175 
176   ## for options with :multi => true, an array default doesn't imply
177   ## a multi-valued argument. for that you have to specify a :type
178   ## as well. (this is how we disambiguate an ambiguous situation;
179   ## see the docs for Parser#opt for details.)
180   disambiguated_default =
181     if opts[:multi] && opts[:default].is_a?(Array) && !opts[:type]
182       opts[:default].first
183     else
184       opts[:default]
185     end
186 
187   type_from_default =
188     case disambiguated_default
189     when Integer; :int
190     when Numeric; :float
191     when TrueClass, FalseClass; :flag
192     when String; :string
193     when IO; :io
194     when Date; :date
195     when Array
196       if opts[:default].empty?
197         raise ArgumentError, _("multiple argument type cannot be deduced from an empty array for '%{value0}'") % { value0: opts[:default][0].class.name }
198       end
199       case opts[:default][0]    # the first element determines the types
200       when Integer; :ints
201       when Numeric; :floats
202       when String; :strings
203       when IO; :ios
204       when Date; :dates
205       else
206         raise ArgumentError, _("unsupported multiple argument type '%{value0}'") % { value0: opts[:default][0].class.name }
207       end
208     when nil; nil
209     else
210       raise ArgumentError, _("unsupported argument type '%{value0}'") % { value0: opts[:default].class.name }
211     end
212 
213   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
214 
215   opts[:type] = opts[:type] || type_from_default || :flag
216 
217   ## fill in :long
218   opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.tr("_", "-")
219   opts[:long] =
220     case opts[:long]
221     when /^--([^-].*)$/
222       $1
223     when /^[^-]/
224       opts[:long]
225     else
226       raise ArgumentError, _("invalid long option name %{name}") % { name: opts[:long].inspect }
227     end
228   raise ArgumentError, _("long option name %{value0} is already taken; please specify a (different) :long") % { value0: opts[:long].inspect } if @long[opts[:long]]
229 
230   ## fill in :short
231   opts[:short] = opts[:short].to_s if opts[:short] unless opts[:short] == :none
232   opts[:short] = case opts[:short]
233     when /^-(.)$/; $1
234     when nil, :none, /^.$/; opts[:short]
235     else raise ArgumentError, _("invalid short option name '%{name}'") % { name: opts[:short].inspect }
236   end
237 
238   if opts[:short]
239     raise ArgumentError, _("short option name %{value0} is already taken; please specify a (different) :short") % { value0: opts[:short].inspect } if @short[opts[:short]]
240     raise ArgumentError, _("a short option name can't be a number or a dash") if opts[:short] =~ INVALID_SHORT_ARG_REGEX
241   end
242 
243   ## fill in :default for flags
244   opts[:default] = false if opts[:type] == :flag && opts[:default].nil?
245 
246   ## autobox :default for :multi (multi-occurrence) arguments
247   opts[:default] = [opts[:default]] if opts[:default] && opts[:multi] && !opts[:default].is_a?(Array)
248 
249   ## fill in :multi
250   opts[:multi] ||= false
251 
252   opts[:desc] ||= desc
253   @long[opts[:long]] = name
254   @short[opts[:short]] = name if opts[:short] && opts[:short] != :none
255   @specs[name] = opts
256   @order << [:opt, name]
257 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
308 def parse cmdline=ARGV
309   vals = {}
310   required = {}
311 
312   if handle_help_and_version
313     opt :version, _("Print version and exit") if @version unless @specs[:version] || @long["version"]
314     opt :help, _("Show this message") unless @specs[:help] || @long["help"]
315   end
316 
317   @specs.each do |sym, opts|
318     required[sym] = true if opts[:required]
319     vals[sym] = opts[:default]
320     vals[sym] = [] if opts[:multi] && !opts[:default] # multi arguments default to [], not nil
321   end
322 
323   resolve_default_short_options if create_default_short_options
324 
325   ## resolve symbols
326   given_args = {}
327   @leftovers = each_arg cmdline do |arg, params|
328     sym = case arg
329     when /^-([^-])$/
330       @short[$1]
331     when /^--no-([^-]\S*)$/
332       @long["[no-]#{$1}"]
333     when /^--([^-]\S*)$/
334       @long[$1] ? @long[$1] : @long["[no-]#{$1}"]
335     else
336       raise CommandlineError, _("invalid argument syntax: '%{arg}'") % { arg: arg }
337     end
338 
339     unless sym
340       next 0 if ignore_invalid_options
341       raise CommandlineError, _("unknown argument '%{arg}'") % { arg: arg } unless sym
342     end
343 
344     if given_args.include?(sym) && !@specs[sym][:multi]
345       raise CommandlineError, _("option '%{arg}' specified multiple times") % { arg: arg }
346     end
347 
348     given_args[sym] ||= {}
349 
350     given_args[sym][:arg] = arg
351     given_args[sym][:params] ||= []
352 
353     # The block returns the number of parameters taken.
354     num_params_taken = 0
355 
356     unless params.nil?
357       if SINGLE_ARG_TYPES.include?(@specs[sym][:type])
358         given_args[sym][:params] << params[0, 1]  # take the first parameter
359         num_params_taken = 1
360       elsif MULTI_ARG_TYPES.include?(@specs[sym][:type])
361         given_args[sym][:params] << params        # take all the parameters
362         num_params_taken = params.size
363       end
364     end
365 
366     num_params_taken
367   end
368 
369   if handle_help_and_version
370     ## check for version and help args
371     raise VersionNeeded if given_args.include? :version
372     raise HelpNeeded if given_args.include? :help
373   end
374 
375   ## check constraint satisfaction
376   @constraints.each do |type, syms|
377     constraint_sym = syms.find { |sym| given_args[sym] }
378     next unless constraint_sym
379 
380     case type
381     when :depends
382       syms.each { |sym| raise CommandlineError, _("--%{value0} requires --%{value1}") % { value0: @specs[constraint_sym][:long], value1: @specs[sym][:long] } unless given_args.include? sym }
383     when :conflicts
384       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) }
385     end
386   end
387 
388   required.each do |sym, val|
389     raise CommandlineError, _("option --%{opt} must be specified") % { opt: @specs[sym][:long] } unless given_args.include? sym
390   end
391 
392   ## parse parameters
393   given_args.each do |sym, given_data|
394     arg = given_data[:arg]
395     params = given_data[:params]
396 
397     opts = @specs[sym]
398     raise CommandlineError, _("option '%{arg}' needs a parameter") % { arg: arg } if params.empty? && opts[:type] != :flag
399 
400     vals["#{sym}_given".intern] = true # mark argument as specified on the commandline
401 
402     case opts[:type]
403     when :flag
404       if arg =~ /^--no-/ and sym.to_s =~ /^--\[no-\]/
405         vals[sym] = opts[:default]
406       else
407         vals[sym] = !opts[:default]
408       end
409     when :int, :ints
410       vals[sym] = params.map { |pg| pg.map { |p| parse_integer_parameter p, arg } }
411     when :float, :floats
412       vals[sym] = params.map { |pg| pg.map { |p| parse_float_parameter p, arg } }
413     when :string, :strings
414       vals[sym] = params.map { |pg| pg.map { |p| p.to_s } }
415     when :io, :ios
416       vals[sym] = params.map { |pg| pg.map { |p| parse_io_parameter p, arg } }
417     when :date, :dates
418       vals[sym] = params.map { |pg| pg.map { |p| parse_date_parameter p, arg } }
419     end
420 
421     if SINGLE_ARG_TYPES.include?(opts[:type])
422       unless opts[:multi]       # single parameter
423         vals[sym] = vals[sym][0][0]
424       else                      # multiple options, each with a single parameter
425         vals[sym] = vals[sym].map { |p| p[0] }
426       end
427     elsif MULTI_ARG_TYPES.include?(opts[:type]) && !opts[:multi]
428       vals[sym] = vals[sym][0]  # single option, with multiple parameters
429     end
430     # else: multiple options, with multiple parameters
431 
432     opts[:callback].call(vals[sym]) if opts.has_key?(:callback)
433   end
434 
435   ## modify input in place with only those
436   ## arguments we didn't process
437   cmdline.clear
438   @leftovers.each { |l| cmdline << l }
439 
440   ## allow openstruct-style accessors
441   class << vals
442     def method_missing(m, *args)
443       self[m] || self[m.to_s]
444     end
445   end
446   vals
447 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
292 def stop_on *words
293   @stop_words = [*words].flatten
294 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
300 def stop_on_unknown
301   @stop_on_unknown = true
302 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
262 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
705 def cloaker &b
706   (class << self; self; end).class_eval do
707     define_method :cloaker_, &b
708     meth = instance_method :cloaker_
709     remove_method :cloaker_
710     meth
711   end
712 end
collect_argument_parameters(args, start_at) click to toggle source
    # File lib/puppet/util/command_line/trollop.rb
659 def collect_argument_parameters args, start_at
660   params = []
661   pos = start_at
662   while args[pos] && args[pos] !~ PARAM_RE && !@stop_words.member?(args[pos]) do
663     params << args[pos]
664     pos += 1
665   end
666   params
667 end
each_arg(args) { |"--#{$1}", [$2]| ... } click to toggle source

yield successive arg, parameter pairs

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