argparse

package module
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 8, 2022 License: MIT Imports: 6 Imported by: 543

README

Golang argparse

GoDoc Go Report Card Coverage Status Build Status

Let's be honest -- Go's standard command line arguments parser flag terribly sucks. It cannot come anywhere close to the Python's argparse module. This is why this project exists.

The goal of this project is to bring ease of use and flexibility of argparse to Go. Which is where the name of this package comes from.

Installation

To install and start using argparse simply do:

$ go get -u -v github.com/akamensky/argparse

You are good to go to write your first command line tool! See Usage and Examples sections for information how you can use it

Usage

To start using argparse in Go see above instructions on how to install. From here on you can start writing your first program. Please check out examples from examples/ directory to see how to use it in various ways.

Here is basic example of print command (from examples/print/ directory):

package main

import (
	"fmt"
	"github.com/akamensky/argparse"
	"os"
)

func main() {
	// Create new parser object
	parser := argparse.NewParser("print", "Prints provided string to stdout")
	// Create string flag
	s := parser.String("s", "string", &argparse.Options{Required: true, Help: "String to print"})
	// Parse input
	err := parser.Parse(os.Args)
	if err != nil {
		// In case of error print error and print usage
		// This can also be done by passing -h or --help flags
		fmt.Print(parser.Usage(err))
	}
	// Finally print the collected string
	fmt.Println(*s)
}
Basic options

Create your parser instance and pass it program name and program description. Program name if empty will be taken from os.Args[0] (which is okay in most cases). Description can be as long as you wish and will be used in --help output

parser := argparse.NewParser("progname", "Description of my awesome program. It can be as long as I wish it to be")

String will allow you to get a string from arguments, such as $ progname --string "String content"

var myString *string = parser.String("s", "string", ...)

Positional arguments can be used like this $ progname value1. See Basic Option Structure and Positionals.

var myString *string = parser.StringPositional(nil)
var myString *string = parser.FilePositional(nil)
var myString *string = parser.FloatPositional(nil)
var myString *string = parser.IntPositional(nil)
var myString *string = parser.SelectorPositional([]string{"a", "b"}, nil)
var myString1 *string = parser.StringPositional(Options{Default: "beep"})

Selector works same as a string, except that it will only allow specific values. For example like this $ progname --debug-level WARN

var mySelector *string = parser.Selector("d", "debug-level", []string{"INFO", "DEBUG", "WARN"}, ...)

StringList allows to collect multiple string values into the slice of strings by repeating same flag multiple times. Such as $ progname --string hostname1 --string hostname2 -s hostname3

var myStringList *[]string = parser.StringList("s", "string", ...)

List allows to collect multiple values into the slice of strings by repeating same flag multiple times (at fact - it is an Alias of StringList). Such as $ progname --host hostname1 --host hostname2 -H hostname3

var myList *[]string = parser.List("H", "hostname", ...)

Flag will tell you if a simple flag was set on command line (true is set, false is not). For example $ progname --force

var myFlag *bool = parser.Flag("f", "force", ...)

FlagCounter will tell you the number of times that simple flag was set on command line (integer greater than or equal to 1 or 0 if not set). For example $ progname -vv --verbose

var myFlagCounter *int = parser.FlagCounter("v", "verbose", ...)

Int will allow you to get a decimal integer from arguments, such as $ progname --integer "42"

var myInteger *int = parser.Int("i", "integer", ...)

IntList allows to collect multiple decimal integer values into the slice of integers by repeating same flag multiple times. Such as $ progname --integer 42 --integer +51 -i -1

var myIntegerList *[]int = parser.IntList("i", "integer", ...)

Float will allow you to get a floating point number from arguments, such as $ progname --float "37.2"

var myFloat *float64 = parser.Float("f", "float", ...)

FloatList allows to collect multiple floating point number values into the slice of floats by repeating same flag multiple times. Such as $ progname --float 42 --float +37.2 -f -1.0

var myFloatList *[]float64 = parser.FloatList("f", "float", ...)

File will validate that file exists and will attempt to open it with provided privileges. To be used like this $ progname --log-file /path/to/file.log

var myLogFile *os.File = parser.File("l", "log-file", os.O_RDWR, 0600, ...)

FileList allows to collect files into the slice of files by repeating same flag multiple times. FileList will validate that files exists and will attempt to open them with provided privileges. To be used like this $ progname --log-file /path/to/file.log --log-file /path/to/file_cpy.log -l /another/path/to/file.log

var myLogFiles *[]os.File = parser.FileList("l", "log-file", os.O_RDWR, 0600, ...)

You can implement sub-commands in your CLI using parser.NewCommand() or go even deeper with command.NewCommand(). Addition of a sub-command implies that a subcommand is required. Sub-commands are always parsed before arguments. If a command has Positional arguments and sub-commands then sub-commands take precedence. Since parser inherits from command, every command supports exactly same options as parser itself, thus allowing to add arguments specific to that command or more global arguments added on parser itself!

You can also dynamically retrieve argument values and if they were parsed:

var myInteger *int = parser.Int("i", "integer", ...)
parser.Parse()
fmt.Printf("%d", *parser.GetArgs()[0].GetResult().(*int))
fmt.Printf("%v", *parser.GetArgs()[0].GetParsed())
Basic Option Structure

The Option structure is declared at argparse.go:

type Options struct {
	Required bool
	Validate func(args []string) error
	Help     string
	Default  interface{}
}

You can set Required to let it know if it should ask for arguments. Or you can set Validate as a lambda function to make it know while value is valid. Or you can set Help for your beautiful help document. Or you can set Default will set the default value if user does not provide a value.

Example:

dirpath := parser.String("d", "dirpath",
			 &argparse.Options{
			 	Required: false,
				Help: "the input files' folder path",
				Default: "input",
			})
Caveats

There are a few caveats (or more like design choices) to know about:

  • Shorthand arguments MUST be a single character. Shorthand arguments are prepended with single dash "-"
  • If not convenient shorthand argument can be completely skipped by passing empty string "" as first argument
  • Shorthand arguments ONLY for parser.Flag() and parser.FlagCounter() can be combined into single argument same as ps -aux, rm -rf or lspci -vvk
  • Long arguments must be specified and cannot be empty. They are prepended with double dash "--"
  • You cannot define two same arguments. Only first one will be used. For example doing parser.Flag("t", "test", nil) followed by parser.String("t", "test2", nil) will not work as second String argument will be ignored (note that both have "t" as shorthand argument). However since it is case-sensitive library, you can work arounf it by capitalizing one of the arguments
  • There is a pre-defined argument for -h|--help, so from above attempting to define any argument using h as shorthand will fail
  • parser.Parse() returns error in case of something going wrong, but it is not expected to cover ALL cases
  • Any arguments that left un-parsed will be regarded as error
Positionals
  • Positional args have a set of effects and conditions:
    • Always parsed after subcommands and non-positional args
    • Always set Required=False
    • Default is only used if the command or subcommand owning the arg Happened
    • Parsed in Command root->leaf left->right order (breadth-first)
      • Top level cmd consumes as many positionals as it can, from left to right
      • Then in a descendeding loop for any command which Happened it repeats
      • Positionals which are not satisfied (due to lack of input args) are not errors
Contributing

Can you write in Go? Then this projects needs your help!

Take a look at open issues, specially the ones tagged as help-wanted. If you have any improvements to offer, please open an issue first to ensure this improvement is discussed.

There are following tasks to be done:

  • Add more examples
  • Improve code quality (it is messy right now and could use a major revamp to improve gocyclo report)
  • Add more argument options (such as numbers parsing)
  • Improve test coverage
  • Write a wiki for this project

However note that the logic outlined in method comments must be preserved as the the library must stick with backward compatibility promise!

Acknowledgments

Thanks to Python developers for making a great argparse which inspired this package to match for greatness of Go

Documentation

Overview

Package argparse provides users with more flexible and configurable option for command line arguments parsing.

Index

Examples

Constants

View Source
const (
	Flag        ArgumentType = 0
	FlagCounter              = 1
	String                   = 2
	Int                      = 3
	Float                    = 4
	File                     = 5
	StringList               = 6
	IntList                  = 7
	FloatList                = 8
	FileList                 = 9
	Selector                 = 10
)
View Source
const DisableDescription = "DISABLEDDESCRIPTIONWILLNOTSHOWUP"

DisableDescription can be assigned as a command or arguments description to hide it from the Usage output

Variables

This section is empty.

Functions

func IsNilFile added in v1.3.1

func IsNilFile(fd *os.File) bool

IsNilFile allows to test whether returned `*os.File` has been initialized with data passed on CLI. Returns true if `fd == &{nil}`, which means `*os.File` was not initialized, false if `fd` is a fully initialized `*os.File` or if `fd == nil`.

Types

type Arg

type Arg interface {
	GetOpts() *Options
	GetSname() string
	GetLname() string
	GetResult() interface{}
	GetPositional() bool
	GetParsed() bool
}

Arg interface provides exporting of arg structure, while exposing it

type ArgumentType added in v1.3.2

type ArgumentType int

enum used to determine the argument type

type Command

type Command struct {
	HelpFunc func(c *Command, msg interface{}) string
	// contains filtered or unexported fields
}

Command is a basic type for this package. It represents top level Parser as well as any commands and sub-commands Command MUST NOT ever be created manually. Instead one should call NewCommand method of Parser or Command, which will setup appropriate fields and call methods that have to be called when creating new command.

func (*Command) ExitOnHelp added in v1.2.0

func (o *Command) ExitOnHelp(b bool)

ExitOnHelp sets the exitOnHelp variable of Parser

func (*Command) File

func (o *Command) File(short string, long string, flag int, perm os.FileMode, opts *Options) *os.File

File creates new file argument, which is when provided will check if file exists or attempt to create it depending on provided flags (same as for os.OpenFile). It takes same as all other arguments short and long names, additionally it takes flags that specify in which mode the file should be open (see os.OpenFile for details on that), file permissions that will be applied to a file and argument options. Returns a pointer to os.File which will be set to opened file on success. On error the Parser.Parse will return error and the pointer might be nil.

func (*Command) FileList added in v1.1.0

func (o *Command) FileList(short string, long string, flag int, perm os.FileMode, opts *Options) *[]os.File

FileList creates new file list argument. This is the argument that is allowed to be present multiple times on CLI. All appearances of this argument on CLI will be collected into the list of os.File values. If no argument provided, then the list is empty. Takes same parameters as File Returns a pointer the list of os.File values.

func (*Command) FilePositional added in v1.4.0

func (o *Command) FilePositional(flag int, perm os.FileMode, opts *Options) *os.File

See func File documentation

func (*Command) Flag

func (o *Command) Flag(short string, long string, opts *Options) *bool

Flag Creates new flag type of argument, which is boolean value showing if argument was provided or not. Takes short name, long name and pointer to options (optional). Short name must be single character, but can be omitted by giving empty string. Long name is required. Returns pointer to boolean with starting value `false`. If Parser finds the flag provided on Command line arguments, then the value is changed to true. Set of Flag and FlagCounter shorthand arguments can be combined together such as `tar -cvaf foo.tar foo`

func (*Command) FlagCounter

func (o *Command) FlagCounter(short string, long string, opts *Options) *int

FlagCounter Creates new flagCounter type of argument, which is integer value showing the number of times the argument has been provided. Takes short name, long name and pointer to options (optional). Short name must be single character, but can be omitted by giving empty string. Long name is required. Returns pointer to integer with starting value `0`. Each time Parser finds the flag provided on Command line arguments, the value is incremented by 1. Set of FlagCounter and Flag shorthand arguments can be combined together such as `tar -cvaf foo.tar foo`

func (*Command) Float

func (o *Command) Float(short string, long string, opts *Options) *float64

Float creates new float argument, which will attempt to parse following argument as float64. Takes as arguments short name (must be single character or an empty string) long name and (optional) options. If parsing fails parser.Parse() will return an error.

func (*Command) FloatList added in v1.1.0

func (o *Command) FloatList(short string, long string, opts *Options) *[]float64

FloatList creates new float list argument. This is the argument that is allowed to be present multiple times on CLI. All appearances of this argument on CLI will be collected into the list of float64 values. If no argument provided, then the list is empty. Takes same parameters as Float Returns a pointer the list of float64 values.

func (*Command) FloatPositional added in v1.4.0

func (o *Command) FloatPositional(opts *Options) *float64

See func Float documentation

func (Command) GetArgs

func (o Command) GetArgs() (args []Arg)

GetArgs exposes Command's args field

func (Command) GetCommands

func (o Command) GetCommands() []*Command

GetCommands exposes Command's commands field

func (Command) GetDescription

func (o Command) GetDescription() string

GetDescription exposes Command's description field

func (Command) GetName

func (o Command) GetName() string

GetName exposes Command's name field

func (Command) GetParent

func (o Command) GetParent() *Command

GetParent exposes Command's parent field

func (*Command) Happened

func (o *Command) Happened() bool

Happened shows whether Command was specified on CLI arguments or not. If Command did not "happen", then all its descendant commands and arguments are not parsed. Returns a boolean value.

func (*Command) Help

func (o *Command) Help(msg interface{}) string

Help calls the overriddable Command.HelpFunc on itself, called when the help argument strings are passed via CLI

Example
parser := NewParser("parser", "")
parser.HelpFunc = func(c *Command, msg interface{}) string {
	return fmt.Sprintf("Name: %s\n", c.GetName())
}
fmt.Println(parser.Help(nil))
Output:

Name: parser
Example (SubcommandDefaulting)
parser := NewParser("parser", "")
parser.HelpFunc = func(c *Command, msg interface{}) string {
	helpString := fmt.Sprintf("Name: %s\n", c.GetName())
	for _, com := range c.GetCommands() {
		// Calls parser.HelpFunc, because command.HelpFuncs are nil
		helpString += com.Help(nil)
	}
	return helpString
}
parser.NewCommand("subcommand1", "")
parser.NewCommand("subcommand2", "")
fmt.Println(parser.Help(nil))
Output:

Name: parser
Name: subcommand1
Name: subcommand2
Example (SubcommandHelpFuncs)
parser := NewParser("parser", "")
parser.HelpFunc = func(c *Command, msg interface{}) string {
	helpString := fmt.Sprintf("Name: %s\n", c.GetName())
	for _, com := range c.GetCommands() {
		// Calls command.HelpFunc, because command.HelpFuncs are not nil
		helpString += com.Help(nil)
	}
	return helpString
}
com1 := parser.NewCommand("subcommand1", "Test description")
com1.HelpFunc = func(c *Command, msg interface{}) string {
	helpString := fmt.Sprintf("Name: %s, Description: %s\n", c.GetName(), c.GetDescription())
	return helpString
}
com2 := parser.NewCommand("subcommand2", "")
com2.String("s", "string", &Options{Required: false})
com2.String("i", "integer", &Options{Required: true})
com2.HelpFunc = func(c *Command, msg interface{}) string {
	helpString := fmt.Sprintf("Name: %s\n", c.GetName())
	for _, arg := range c.GetArgs() {
		helpString += fmt.Sprintf("\tLname: %s, Required: %t\n", arg.GetLname(), arg.GetOpts().Required)
	}
	return helpString
}
fmt.Print(parser.Help(nil))
fmt.Print(com1.Help(nil))
fmt.Print(com2.Help(nil))
Output:

Name: parser
Name: subcommand1, Description: Test description
Name: subcommand2
	Lname: string, Required: false
	Lname: integer, Required: true
Name: subcommand1, Description: Test description
Name: subcommand2
	Lname: string, Required: false
	Lname: integer, Required: true

func (*Command) Int

func (o *Command) Int(short string, long string, opts *Options) *int

Int creates new int argument, which will attempt to parse following argument as int. Takes as arguments short name (must be single character or an empty string) long name and (optional) options. If parsing fails parser.Parse() will return an error.

func (*Command) IntList added in v1.1.0

func (o *Command) IntList(short string, long string, opts *Options) *[]int

IntList creates new integer list argument. This is the argument that is allowed to be present multiple times on CLI. All appearances of this argument on CLI will be collected into the list of integers. If no argument provided, then the list is empty. Takes same parameters as Int Returns a pointer the list of integers.

func (*Command) IntPositional added in v1.4.0

func (o *Command) IntPositional(opts *Options) *int

See func Int documentation

func (*Command) List

func (o *Command) List(short string, long string, opts *Options) *[]string

List creates new list argument. This is the argument that is allowed to be present multiple times on CLI. All appearances of this argument on CLI will be collected into the list of default type values which is strings. If no argument provided, then the list is empty. Takes same parameters as String. Returns a pointer the list of strings.

func (*Command) NewCommand

func (o *Command) NewCommand(name string, description string) *Command

NewCommand will create a sub-command and propagate all necessary fields. All commands are always at the beginning of the arguments. Parser can have commands and those commands can have sub-commands, which allows for very flexible workflow. All commands are considered as required and all commands can have their own argument set. Commands are processed Parser -> Command -> sub-Command. Arguments will be processed in order of sub-Command -> Command -> Parser.

func (*Command) Selector

func (o *Command) Selector(short string, long string, options []string, opts *Options) *string

Selector creates a selector argument. Selector argument works in the same way as String argument, with the difference that the string value must be from the list of options provided by the program. Takes short and long names, argument options and a slice of strings which are allowed values for CLI argument. Returns a pointer to a string. If argument is not required (as in argparse.Options.Required), and argument was not provided, then the string is empty.

func (*Command) SelectorPositional added in v1.4.0

func (o *Command) SelectorPositional(allowed []string, opts *Options) *string

See func Selector documentation

func (*Command) String

func (o *Command) String(short string, long string, opts *Options) *string

String creates new string argument, which will return whatever follows the argument on CLI. Takes as arguments short name (must be single character or an empty string) long name and (optional) options

func (*Command) StringList added in v1.1.0

func (o *Command) StringList(short string, long string, opts *Options) *[]string

StringList creates new string list argument. This is the argument that is allowed to be present multiple times on CLI. All appearances of this argument on CLI will be collected into the list of strings. If no argument provided, then the list is empty. Takes same parameters as String Returns a pointer the list of strings.

func (*Command) StringPositional added in v1.4.0

func (o *Command) StringPositional(opts *Options) *string

See func String documentation

func (*Command) Usage

func (o *Command) Usage(msg interface{}) string

Usage returns a multiline string that is the same as a help message for this Parser or Command. Since Parser is a Command as well, they work in exactly same way. Meaning that usage string can be retrieved for any level of commands. It will only include information about this Command, its sub-commands, current Command arguments and arguments of all preceding commands (if any)

Accepts an interface that can be error, string or fmt.Stringer that will be prepended to a message. All other interface types will be ignored

type Options

type Options struct {
	Required bool
	Validate func(args []string) error
	Help     string
	Default  interface{}
	// contains filtered or unexported fields
}

Options are specific options for every argument. They can be provided if necessary. Possible fields are:

Options.positional - tells Parser that the argument is positional (implies Required). Set to true by using *Positional functions. Positional arguments must not have arg name preceding them and must come in a specific order. Positionals are parsed breadth-first (left->right from Command tree root to leaf) Positional sets Shortname="", ignores Required Positionals which are not satisfied will be nil but no error will be thrown Defaults are only set for unparsed positionals on commands which happened Use arg.GetParsed() to detect if arg was satisfied or not

Options.Required - tells Parser that this argument is required to be provided. useful when specific Command requires some data provided.

Options.Validate - is a validation function. Using this field anyone can implement a custom validation for argument. If provided and argument is present, then function is called. If argument also consumes any following values (e.g. as String does), then these are provided as args to function. If validation fails the error must be returned, which will be the output of `Parser.Parse` method.

Options.Help - A help message to be displayed in Usage output. Can be of any length as the message will be formatted to fit max screen width of 100 characters.

Options.Default - A default value for an argument. This value will be assigned to the argument at the end of parsing in case if this argument was not supplied on command line. File default value is a string which it will be open with provided options. In case if provided value type does not match expected, the error will be returned on run-time.

type Parser

type Parser struct {
	Command
}

Parser is a top level object of argparse. It MUST NOT ever be created manually. Instead one should use argparse.NewParser() method that will create new parser, propagate necessary private fields and call needed functions.

func NewParser

func NewParser(name string, description string) *Parser

NewParser creates new Parser object that will allow to add arguments for parsing It takes program name and description which will be used as part of Usage output Returns pointer to Parser object

func (*Parser) DisableHelp added in v1.2.0

func (o *Parser) DisableHelp()

DisableHelp removes any help arguments from the commands list of arguments This prevents prevents help from being parsed or invoked from the argument list

func (*Parser) Parse

func (o *Parser) Parse(args []string) error

Parse method can be applied only on Parser. It takes a slice of strings (as in os.Args) and it will process this slice as arguments of CLI (the original slice is not modified). Returns error on any failure. In case of failure recommended course of action is to print received error alongside with usage information (might want to check which Command was active when error happened and print that specific Command usage). In case no error returned all arguments should be safe to use. Safety of using arguments before Parse operation is complete is not guaranteed.

func (*Parser) SetHelp added in v1.2.0

func (o *Parser) SetHelp(sname, lname string)

SetHelp removes the previous help argument, and creates a new one with the desired sname/lname

Directories

Path Synopsis
examples
validation
Validation functions always get a string slice as input and an error as the return type the string slice represents the 1+ instances of data passed to the argument the error represents what is returned in case the validation finds an error it must be nil if everything is ok
Validation functions always get a string slice as input and an error as the return type the string slice represents the 1+ instances of data passed to the argument the error represents what is returned in case the validation finds an error it must be nil if everything is ok

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL