argparse

package
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Jan 19, 2025 License: MIT Imports: 17 Imported by: 0

README

argparse

A type safe, extensible CLI argument parsing utility package.

https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L49-L74 Example usage of the argparse package

Usage

The above example shows the general usage of this package. In the example there are three main parts to consider:

  1. An ArgBuilder is created and it is populated with arguments. This stage is where the vast majority of your interaction with the package will take place, and is explained in more detail in the next section.
  2. The ArgBuilder makes a Parser. Optionally this is where sub parsers could be added if you were using them.
  3. The Parser is then used to parse a slice of strings which is translated into a sequence of tokens.

Argument Builder: Primitive Types

Primitive types are the most straight forward. Shown below is how to add an integer argument. The translators.BuiltinInt type is responsible for parsing an integer from the string value supplied by the CLI. Analogous types are available for all primitive types, all following the Builtin<type> format.

https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L20-L21 Integer argument

The above example provides an argument with no options, meaning all the default options will be used. Shown below is how to provide your own set of options.

https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L55-L61 Integer argument with options

The following options can be provided all through setter methods similar to the ones shown in the example above:

  1. shortName: The single character flag that the argument can be specified with as a short hand for the provided long name.
  2. description: The description that will be printed on the help menu.
  3. required: A boolean indicating if the argument is required or not.
  4. defaultVal: The value that should be returned if the argument is not provided on the CLI.
  5. translator: The translator that should be used when translating the CLI string to the appropriately typed value.
  6. argType: The type of argument. This value tells the parser what semantics are valid for the argument.

The available argument types are as follows:

  1. ValueArgType: Represents a flag type that must accept a single value as an argument and must only be supplied once.
  2. MultiValueArgType: Represents a flag type that can accept many values as an argument and must only be supplied once. At least one argument must be supplied.
  3. FlagArgType: Represents a flag type that must not accept a value and must only be supplied once.
  4. MultiFlagArgType: Represents a flag type that must not accept a value and may be supplied many times.

Argument Builder: Out of the Box Argument Types

The ArgBuilder also has several helper functions and translators for common CLI argument types.

  1. Flag arguments. This will return true if the flag is provided. It does accept any values.

https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L94-L100

  1. Flag counter argument. This will return an integer equal to the number of times that the flag was provided. It does not accept any values.

https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L133-L138

  1. List argument. This will build up a list of all the values that were provided with the argument. Many values can be provided with a single argument or many flags can be provided with a single argument, as shown in the example arguments below the argument example.

https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L171-L188 https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L193

  1. List argument with a predefined set of allowed values. This will build up a list of all the values that were provided with the argument, provided that they are in the allowed list of values. Many values can be provided with a single argument or many flags can be provided with a single argument, as shown in the example arguments below the argument example. Note that given the design of this translator the list can contain any type, as long as the underlying type has a translator of it's own.

https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L212-L232 https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L239

  1. Selector argument. This will accept a single value as long as that value is in the predefined set of allowed values. As with the list argument, the selector translator can work with any type as long as it has an underlying translator of it's own.

https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L260-L278

  1. File argument. This will accept a single string value and verify that the supplied string is a path that exists as a file.

https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L398-L403

  1. Directory argument. This will accept a single string value and verify that the supplied string is a path that exists as a directory.

https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L427-L432

  1. File open argument. This will accept a single string value and will attempt to make the supplied file with the given file mode and permissions.

https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L456-L468

  1. Mkdir argument. This will accept a single string value and will attempt to make the directory(s) that are denoted by the string value.

https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L493-L501

  1. Enum argument. This will accept a single string value and will attempt to translate it to the underlying enum value given the enum type it was supplied with through the generic parameters.

https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L302-L312

Argument Builder: Custom Types

Due to using generics, the argument builder can accept arguments of custom types as long as there is a translator for that type. For examples of how to implement translators refer to the stateless translator example as well as the stateful translator example. Any custom types defined outside of this package will use the AddArg function to add arguments.

To support a custom type the Translate method on the translator will simply need to return the custom type. Support for custom translators and types allows for a completely type safe translation of the CLI arguments into values that your program can work with.

Argument Builder: Computed Arguments

Computed arguments provide a way for the argument parser to set values that were not directly provided by the CLI, potentially computing values based on the provided CLI arguments. The example below shows how to add a computed argument to the parser.

https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/examples/SimpleExamples_test.go#L354-L357

Much like translators for arguments, computers are needed to set computed values. Also like translators, computers are expected to return a value, this time the value that is returned is the result of a computation rather than a translation. Yet another similarity is that computed arguments support custom types in all the same ways that translators do. For an example of how to implement a custom computer refer to the custom computer example.

Computed arguments do have one advantage over normal (translated) arguments. All computed arguments are added to a tree like data structure following the same ordering that any sub-parsers were added in. Given this tree like data structure computed arguments are then evaluated bottom up, left to right. With this organized evaluation ordering it is possible to evaluate arbitrary expressions. This will likely never be useful and an argument could be made that this should never be done, but the capability is there. For an example of this evaluation refer to the sub-parsers examples.

Sub-Parsers

Several different parsers, each with there own set of arguments, can be separately built and then combined into one larger parser. When adding one parser to another the parsers are added as children, or sub parsers, to the main parser. A couple examples of sub-parsers are shown in the sub-parsers examples file. There are a couple rules that dictate what happens when sub-parsers are added:

  1. All non-computed arguments are added to one single global namespace. This means that all long argument and short argument names must be unique among all of the sub-parers.
  2. Computed arguments are added in a tree like data structure that mirrors the structure created by the function calls for adding sub-parsers. The advantage of this is explained in the previous section.

There are several sub-parsers that are provided out-of-the-box for common CLI needs.

  1. Help: this adds the -h and --help flag arguments which when encountered will stop all further parsing and print the help menu.
  2. Verbosity: this adds the -v and --verbose flag counter arguments which can be used to set a verbosity level for a running application.

Argument Config Files

An argument config file format is provided out of the box for the case where the list of arguments a program needs gets very large. This argument config file provides a place to put arguments and allows for a basic form of grouping. The --config long argument name is a reserved name and will be available to every argparse parser. It is used to specify a path to an argument config file, as shown below.

# Both an equals sign and a space are allowed
./<prog> --config /path/toFile
./<prog> --config=/path/toFile

The format of the config file is best shown by example.

https://github.com/barbell-math/util/blob/d4b081dad4b35c30ca0bb67e6ed603ca04059a3b/src/argparse/testData/ValidConfigFile.txt#L1-L17 An example argument config file

The above config file is equivalent to the cmd shown below:

./<prog> --Name0 Value0 --Group1Group2Name1 Value1 --Group1Group2Group3Name2 Value2 --Group1Group2Name3 Value3 --Group1Name4 Value4 --Group1Name5 Value5 --Name6 Value6

There are several points of note:

  1. Notice how in the above example the group names get prepended to the argument names to create the final argument name. Nested group names are appended in the order they are nested.
  2. All names are expected to be valid long names that will be recognized by the argument parser after group names are done being prepended. Short names were disallowed to make things clearer in the config file. There is no sense in single letter names in a config file.
  3. Arguments will be parsed in the top down order they are given in the file regardless of how deeply nested they are. Duplicating single value arguments will result in an error. Duplicating multi value arguments will not result in an error.
  4. Many argument config files can be specified, though this is discouraged as it is easy to duplicate arguments between the files.
  5. Standard cmd line arguments can be used in conjunction with config files. As usual, if single value arguments are duplicated an error will be returned and if multi value arguments are duplicated no error will be returned.

A more practical example of using a config file is shown using the [db] packages argparse interface. The below config file and cmd line arguments are equivalent.

# ./Config.txt
db {
    User <user>
    EnvPswdVar <var>
    NetLoc <url>
    Port <port num>
    Name <db name>
}
./<prog> --dbUser <user> --dbEnvPswdVar <var> --dbNetLoc <url> --dbPort <port num> --dbName <db name>
# ... would be the same as ...
./<prog> --config ./Config.txt

Further Reading:

  1. Widgets
  2. Database Package

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	InvalidArgType           = errors.New("Invalid ArgType")
	ARG_TYPE       []ArgType = []ArgType{
		UnknownArgType,
		ValueArgType,
		MultiValueArgType,
		FlagArgType,
		MultiFlagArgType,
	}
)
View Source
var (
	ParserConfigErr       = errors.New("An error occurred setting up the parser")
	ReservedShortNameErr  = errors.New("Reserved short name used")
	ReservedLongNameErr   = errors.New("Reserved long name used")
	DuplicateShortNameErr = errors.New("Duplicate short name")
	DuplicateLongNameErr  = errors.New("Duplicate long name")
	LongNameToShortErr    = errors.New("Long name must be more than one char")

	ParserCombinationErr = errors.New("Could not combine parsers")

	ParsingErr                     = errors.New("An error occurred parsing the supplied arguments")
	ExpectedArgumentErr            = errors.New("Expected an argument (short or long)")
	ExpectedValueErr               = errors.New("Expected a value")
	UnrecognizedShortArgErr        = errors.New("Unrecognized short argument")
	UnrecognizedLongArgErr         = errors.New("Unrecognized long argument")
	EndOfTokenStreamErr            = errors.New("The end of the token stream was reached")
	ArgumentPassedMultipleTimesErr = errors.New("Argument was passed multiple times but was expected only once")

	ParserConfigFileErr       = errors.New("An error occurred parsing a parser config file")
	ParserConfigFileSyntaxErr = errors.New("Syntax error")

	ArgumentTranslationErr = errors.New("An error occurred translating the supplied argument")
	MissingRequiredArgErr  = errors.New("Required argument missing")
	ComputedArgumentErr    = errors.New("An error occurred calculating a computed argument")

	// The error returned when the help menu is displayed, indicating that the
	// parsing the arguments did not end in a "true" error but also did not
	// completely finish.
	HelpErr = errors.New("Help flag specified. Stopping.")
)
View Source
var (
	InvalidFlag        = errors.New("Invalid flag")
	FLAG        []flag = []flag{
		unknownFlag,
		shortSpaceFlag,
		shortEqualsFlag,
		longSpaceFlag,
		longEqualsFlag,
		configEqualsFileFlag,
		configSpaceFileFlag,
	}
)
View Source
var (
	InvalidTokenType             = errors.New("Invalid tokenType")
	TOKEN_TYPE       []tokenType = []tokenType{
		unknownTokenType,
		shortFlagToken,
		longFlagToken,
		valueToken,
	}
)

Functions

func AddArg

func AddArg[T any, U translators.Translater[T]](
	val *T,
	builder *ArgBuilder,
	longName string,
	opts *opts[T, U],
)

Appends an argument to the supplied builder without performing any validation of the argument or builder as a whole.

If opts is Nil then the opts will be populated with the default values from calling NewOpts.

func AddComputedArg

func AddComputedArg[T any, U computers.Computer[T]](
	val *T,
	builder *ArgBuilder,
	computer U,
)

Appends a computed argument to the supplied builder without performing any validation of computation of the argument or builder as a whole.

func AddEnum added in v0.0.2

func AddEnum[E enum.Value, EP enum.Pntr[E]](
	val *E,
	builder *ArgBuilder,
	longName string,
	opts *opts[E, translators.Enum[E, EP]],
)

Appends a enum selector to the supplied builder without performing any validation of the argument or builder as a whole. Enum selector arguments accept a value that must map to a valid enum value of the supplied enum type.

func AddFlag

func AddFlag(
	val *bool,
	builder *ArgBuilder,
	longName string,
	opts *opts[bool, translators.Flag],
)

Appends a flag argument to the supplied builder without performing any validation of the argument or builder as a whole. Counter flags can only be supplied once.

The arg type of the opts struct will be set to FlagArgType.

If opts is Nil then the opts will be populated with the default values from calling NewOpts.

func AddFlagCntr

func AddFlagCntr[T mathBasic.Int | mathBasic.Uint](
	val *T,
	builder *ArgBuilder,
	longName string,
	opts *opts[T, *translators.FlagCntr[T]],
)

Appends a flag counter argument to the supplied builder without performing any validation of the argument or builder as a whole. Counter flags can be supplied many times. The counter will represent the total number of times the flag was supplied.

The arg type of the opts struct will be set to MultiFlagArgType.

The translator value in the opts struct will be initialized to a zero-valued [translator.FlagCntr].

If opts is Nil then the opts will be populated with the default values from calling NewOpts.

func AddListArg

func AddListArg[T any, U translators.Translater[T], W widgets.BaseInterface[T]](
	val *[]T,
	builder *ArgBuilder,
	longName string,
	opts *opts[[]T, *translators.ListValues[T, U, W]],
)

Appends a list argument to the supplied builder without performing any validation of the argument or builder as a whole. List arguments accept many values for a single flag and will return a slice of all the translated values.

The arg type of the opts struct will be set to MultiFlagArgType.

If opts is Nil then the opts will be populated with the default values from calling NewOpts.

func AddSelector

func AddSelector[T any, U translators.Translater[T], W widgets.BaseInterface[T]](
	val *T,
	builder *ArgBuilder,
	longName string,
	opts *opts[T, translators.Selector[T, U, W]],
)

Appends a selector argument to the supplied builder without performing any validation of the argument or builder as a whole. Selector arguments accept one value that must be one of a predefined set of values.

The arg type of the opts struct will be set to ValueArgType.

If opts is Nil then the opts will be populated with the default values from calling NewOpts.

func NewFlag

func NewFlag() flag

func NewOpts

func NewOpts[T any, U translators.Translater[T]]() *opts[T, U]

Returns a new opts struct initialized with the default values.

func NewTokenType

func NewTokenType() tokenType

Types

type Arg

type Arg struct {
	// contains filtered or unexported fields
}

Represents a single argument from the cmd line interface and all the options associated with it.

type ArgBuilder

type ArgBuilder struct {
	// contains filtered or unexported fields
}

Used to create a list of arguments that are then used to build the parser.

func (*ArgBuilder) ToParser

func (b *ArgBuilder) ToParser(progName string, progDesc string) (Parser, error)

Creates a parser using the arg builder. Note that the arg builder will be modified and should not be used again after calling ToParser. The previously added arguments will be validated. Validation can return one of the below errors, all warpped in a top level ParserConfigErr:

type ArgType

type ArgType int
const (
	//gen:enum string UnknownArgType
	UnknownArgType ArgType = iota
	// Represents a flag type that must accept a value as an argument and must
	// only be supplied once.
	//gen:enum string ValueArgType
	ValueArgType
	// Represents a flag type that can accept many values as an argument and
	// must only be supplied once. At least one argument must be supplied.
	//gen:enum string MultiValueArgType
	MultiValueArgType
	// Represents a flag type that must not accept a value and must only be
	// supplied once.
	//gen:enum string FlagArgType
	FlagArgType
	// Represents a flag type that must not accept a value and may be supplied
	// many times.
	//gen:enum string MultiFlagArgType
	MultiFlagArgType
)

func NewArgType

func NewArgType() ArgType

func (*ArgType) Eq

func (_ *ArgType) Eq(l *ArgType, r *ArgType) bool

Returns true if l equals r. Uses the Eq operator provided by the widgets.BuiltinInt widget internally.

func (*ArgType) FromString

func (o *ArgType) FromString(s string) error

func (*ArgType) Hash

func (_ *ArgType) Hash(other *ArgType) hash.Hash

Returns a hash to represent other. The hash that is returned will be supplied by the widgets.BuiltinInt widget internally.

func (ArgType) MarshalJSON

func (o ArgType) MarshalJSON() ([]byte, error)

func (ArgType) String

func (o ArgType) String() string

func (*ArgType) UnmarshalJSON

func (o *ArgType) UnmarshalJSON(b []byte) error

func (ArgType) Valid

func (o ArgType) Valid() error

func (*ArgType) Zero

func (_ *ArgType) Zero(other *ArgType)

Zeros the supplied value. The operation that is performed will be determined by the widgets.BuiltinInt widget internally.

type ArgvIter

type ArgvIter iter.Iter[string]

Represents an sequence of strings that can be translated into a sequence of tokens.

func ArgvIterFromSlice

func ArgvIterFromSlice(argv []string) ArgvIter

func (ArgvIter) ToIter

func (a ArgvIter) ToIter() iter.Iter[string]

func (ArgvIter) ToTokens

func (a ArgvIter) ToTokens() tokenIter

Translates the sequence of strings into tokens. No validation is done to check that the stream of tokens is valid.

type ComputedArg

type ComputedArg struct {
	// contains filtered or unexported fields
}

Represents an argument value that is computed from other arguments rather than being supplied on the cmd line interface.

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

The type that will take a token stream generated from a sequence of strings and perform all translating and computing tasks. See the [examples] package and README.md for more in depth information.

func NewHelpParser

func NewHelpParser() *Parser

Creates a parser that will display the help menu when either -h or --help are supplied.

func NewVerbosityParser

func NewVerbosityParser[T basic.Int | basic.Uint](val *T) *Parser

Creates a parser that has -v and --verbose flags. These flags can be supplied many times and the total count of the number of times the argument was supplied will be placed in val.

func (*Parser) AddSubParsers

func (p *Parser) AddSubParsers(others ...*Parser) error

Adds sub-parsers to the current parser. All arguments are placed in a global namespace and must be unique. No long or short names can collide. All computed args are added to a tree like data structure, which is used to maintain the desired bottom up order of execution for computed arguments.

func (*Parser) Help

func (p *Parser) Help() string

Returns a string representing the help menu.

func (*Parser) Parse

func (p *Parser) Parse(t tokenIter) error

Parses the token stream given to it. This is a two step process. The steps are as follows:

  1. Consume the tokens and translate all received values, saving the results

to the desired locations.

  1. Compute all computed arguments in a bottom-up, left-right fashion.

If an error occurs in this process it will be returned wrapped in a top level ParsingErr. The only exception to this will be the HelpErr, which will stop all further parsing, print the help menu, and return. Any tokens that were present before the help flag will be translated.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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