Git Product home page Git Product logo

subcommands's Introduction

subcommands

GoDoc
Subcommands is a Go package that implements a simple way for a single command to have many subcommands, each of which takes arguments and so forth.

This is not an official Google product.

Usage

Set up a 'print' subcommand:

import (
  "context"
  "flag"
  "fmt"
  "os"
  "strings"

  "github.com/google/subcommands"
)

type printCmd struct {
  capitalize bool
}

func (*printCmd) Name() string     { return "print" }
func (*printCmd) Synopsis() string { return "Print args to stdout." }
func (*printCmd) Usage() string {
  return `print [-capitalize] <some text>:
  Print args to stdout.
`
}

func (p *printCmd) SetFlags(f *flag.FlagSet) {
  f.BoolVar(&p.capitalize, "capitalize", false, "capitalize output")
}

func (p *printCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
  for _, arg := range f.Args() {
    if p.capitalize {
      arg = strings.ToUpper(arg)
    }
    fmt.Printf("%s ", arg)
  }
  fmt.Println()
  return subcommands.ExitSuccess
}

Register using the default Commander, also use some built in subcommands, finally run Execute using ExitStatus as the exit code:

func main() {
  subcommands.Register(subcommands.HelpCommand(), "")
  subcommands.Register(subcommands.FlagsCommand(), "")
  subcommands.Register(subcommands.CommandsCommand(), "")
  subcommands.Register(&printCmd{}, "")

  flag.Parse()
  ctx := context.Background()
  os.Exit(int(subcommands.Execute(ctx)))
}

subcommands's People

Contributors

adjackura avatar akhilpai avatar creachadair avatar darkfeline avatar donatj avatar eliben avatar ianlewis avatar kalyan02 avatar kazuminn avatar kotakanbe avatar nguyen-phillip avatar shawnps avatar takebayashi avatar tcyrus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

subcommands's Issues

Subcommand suggestions for typos

I'd like for Commander.Execute to print a suggestion if the given subcommand looks like a typo similar to what Git does:

$ git brunch
git: 'brunch' is not a git command. See 'git --help'.

The most similar command is
        branch

I have a simple implementation of this using https://github.com/agext/levenshtein that I will attach as a PR.

API to print usage should be public, so commands can invoke it

If a command requires positional arguments, it can be necessary and appropriate to print usage.

Right now, relevant API calls such as explain() are private. There should be some mechanism for a subcommand to validate its argument list and invoke this package's existing mechanisms for generating help/usage should those positional arguments be incorrect.

Prevent registering homonymous commands

I could find this behavior when using this library in conjunction with go-cmdtest.

Because of how test suites in go-cmdtest work, a subcommand can be tested several times with different flags and arguments. In order for this to happen, for each variation, the program's main function is called, what consists of registering commands, so subcommands.Register is called more than once for the same command in a single go test run.

Because go-cmdtest overrides os.Stdout and os.Stderr before each test, I correctly set them to my type that implements subcommands.Command just before registering it, so it points to the correct output set by go-cmdtest.

However, because I register commands with the same name more than once and they are stored in the same subcommands.Commander (subcommands.DefaultCommander), when executing the subcommand more than once, only the first one registered is picked.

Of course, I already solved my problem by creating a new subcommands.Commander in the main function, but if a subcommands.Commander can only execute the first command registered for a list of homonymous commands, there is no point of letting more than one homonym to be registered.

So, for fixing this, I think there is no point in discarding the newer ones. I guess the best solution would be to override a subcommand when another of same name already exists. This way, no unused homonyms will get appended.

println is redirected to stderr

When using println in the subcommand, the output is directed to stderr instead of stdout. This is important for command line utilities which differentiate between status messages and output which should be piped elsewhere, e.g. a file or another process.

Kubectl / git like plugin system

It would be nice to be able to specify a prefix and if a subcommand is not found look for an executable in the PATH that matches prefix-subcommand and execute it if found, passing any args and flags to that executable. Like git and kubectl do.

Q: go1.7 context

Do you have some plan to use context from standard library for go1.7 ?

Tag versions correctly

subcommands support Go modules, however some versions are tagged incorrectly, since the "v" prefix is missing.
For example, we have a release v1.2.0 but the corresponding tag is 1.2.0.

go get
go: github.com/google/[email protected]: reading github.com/google/subcommands/go.mod at revision v1.2.0: unknown revision v1.2.0

Prefix matching for subcommands

I'd like to be able to invoke a subcommand using a unique prefix instead of having to type an exact match. E.g., if "foo" defines subcommands "dobaz", "doqux" and "dropit", then

"foo dob", "foo doba" -> "foo dobaz"
"foo doq", "foo doqu" -> "foo doqux"
"foo dr", "foo dro", ... -> "foo dropit"
"foo d", "foo do" -> error, ambiguous prefix

This is sufficiently different behavior from previously that it should probably be optional, but I'm not sure how best to fit it in.

I have an implementation I use in my own code that I can contribute.

Tag versions correctly

subcommands support Go modules, however the versions are tagged incorrectly, since the "v" prefix is missing.

Commander misses initializing flagset output

Description

In the *Commander.Execute method, a new flag.FlagSet is initialized for the command to run. However, the FlagSet.output of that FlagSet is not initialized, which causes potential errors to be reported by f.failf(), which in turn invokes f.Output(), which falls back to os.Stderr because the field is not initialized.

This causes the error output not to match the commander .Error property, preventing capture/redirection especially during tests.

What should happen

The output of the newly created FieldSet should be set to either the cdr.topFlags output, or to cdr.Error during initialization, with the latter slightly being more logical.

Programmatically Generating Autocompletion Hints

I'm a big fan of subcommands for its simplicity, so I'm curious about the best way to go about adding support for this feature in the project without ruining the simplicity. The basic idea is I would like to create a generator that can walk the registered commands and their flags and generate bash (1, 2) and zsh autocompletion hints for any given binary programmatically.

I see there being three main ways this could be done:

  1. Contribute a subcommands.Command implementation directly to subcommands similar to subcommands.FlagsCommand that emits the necessary bash magic. This has the advantage that the API of subcommands can remain generally the same while avoiding exporting additional state about the implementation. And it introduces a potential disadvantage of tightly coupling bash and zsh shenanigans into an otherwise spartan project.
  2. Export the needed data by expanding the surface of (*subcommands.Commander).VisitCommands such that it includes the *flag.FlagSet and importance metadata about all registered commands. This would be needed to meaningfully attribute command line flags per subcommand in an autocompletion generator. The advantage is that a bash and zsh generator could be built outside of subcommands. The disadvantage is that it exposes some internal implementation details that are — otherwise — innocuous while likely breaking API compatibility with existing users of (*subcommands.Commander).VisitCommands if the signature changes. Alternatively a new visiting method could be introduced that exposes this data while keeping the original ones as-is.
  3. For completeness: there is a hybrid of nos. 1 and 2, wherein the needed exposition capabilities of no. 2 could be exposed through an internal package. Then, autocompletion hints code could be provided in a contrib directory rooted from the same parent directory that the internal one lives under. This has the advantage of keeping the public API parsimonious with the disadvantage of a contrib directory hierarchy. I'm not crazy about this option.

What do you folks think? Any preferences between the three, or am I overlooking a better alternative? I lean more toward no. 2.

Feature Request: Allow hidden commands

I work for a team at Google that uses this package. It's a command line tool oriented at other Googlers and but we often find ourselves wanting to use the same binary for developer-only tasks that are not displayed to users through the help command.

Feature: Allow to run default command without showing usage

Hi,

I found this tiny library that fits my needs much more than a bigger library which I wouldn't use more than 1%. So first of all, thanks for your great work.

I happened to find myself in a situation where I wanted a default command to be executed if no subcommand was provided. The easiest wat I found is to check that the subcommands.Execute(context.Context) func returned a subcommands.ExitUsageError.

This works well, except that it displays the list of all available commands due to this part of the code

if cdr.topFlags.NArg() < 1 {
	cdr.topFlags.Usage()
	return ExitUsageError
}

What I need is a default command that does what it does, and subcommands that does other things too. If I have a default command, I dont want subcommands to prompt commands usage.

The main problems I found are

  • I don't want this feature to impact people using subcommands
  • I want to have a distinction between "No subcommand was provided" and "The subcommand provided does not exist"

An easy to implement solution that may impact user (but guarentee a differentiation between no subcommand and invalid subcommand) would be

const (
	ExitSuccess ExitStatus = iota
	ExitFailure
	ExitUsageError
	ExitNoArgs
)

// MustShowUsageWhenNoArg when set to true (default) display command usage
// when no argument is set. This allows to execute a command by default if
// no argument is provided when calling the cli
var MustShowUsageWhenNoArg = true

func (cdr *Commander) Execute(ctx context.Context, args ...interface{}) ExitStatus {
	if cdr.topFlags.NArg() < 1 {
		if MustShowUsageWhenNoArg {
			cdr.topFlags.Usage()
		}
		return ExitNoArgs
	}
	// ...
}

These changes would allow to use subcommands like

// disable default behavior
subcommands.MustShowUsageWhenNoArg = false
exitCode := subcommands.Execute(ctx)

if exitCode == subcommands.ExitNoArgs {
    defaultCmd.Execute(ctx, flag.NewFlagSet(serverCmd.Name(), flag.ContinueOnError))
}

Another approach could be to store directly the command to execute in case no subcommand is set by the user

// DefaultCommand is the command to execute by default if no subcommand was provided
var DefaultCommand Command

func (cdr *Commander) Execute(ctx context.Context, args ...interface{}) ExitStatus {
	if cdr.topFlags.NArg() < 1 {
		if nil != DefaultCommand {
			DefaultCommand.Execute(ctx, flag.NewFlagSet(DefaultCommand.Name(), flag.ContinueOnError), args...)
		} else {
			cdr.topFlags.Usage()
		}
		return ExitUsageError
	}
	// ...
}

This approach has the less impact on the already existing code using this lib.

The last option would be to check on my code the number of arguments provided. If none, I execute my command mannualy, otherwise, I let subcommands.Execute handle the input.

I may miss some subtilities of the subcommands library, so please let me know if there is a another way to delegate the "No args" case to a part of my code

package context: unrecognized import path "context" (import path does not begin with hostname)

It looks like a71b91e238406bd68766ee52db63bebedce0e9f6 broke using this library in, at least, go1.6.3

$ go get github.com/google/subcommands    
package context: unrecognized import path "context" (import path does not begin with hostname)

My golang version:

$ go version  
go version go1.6.3 linux/amd64

$ rpm -q golang
golang-1.6.3-3.fc24.x86_64

I'm using the latest golang in the fedora24 yum repos

No documentation for top level flags

There seems to be facilities for adding top level flags given that you can define them as normal with the flags library and then mark them important with the given functions, but I can't seem to find out how to get the value in a subcommand. For example, if I have the following command:

$ foo --verbose bar --frobnicate

then I would expect for bar to get the frobnicate flag set true, which I have figured out how to do, but in this case I expect it to also have access to the verbose flag. How can I make sure that bar has access to the top level flags defined outside it's scope? They seem to be available in topFlags but I can't figure out how to access that from a subcommand.

Help mentions flags-subcommand even if it doesn't exists

I don't want a "flags" subcommand - if I have any top-level flags, I would rather they are all treated as "important". However, even if I don't add a flags subcommand or not, the help-subcommand always prints

Use "nbd flags" for a list of top-level flags

That shouldn't be printed, unless there is actually a flags subcommand.

proposal: Execute() (ExitStatus, error)

I think that it will be useful for many users of this superb package.

func main() {
	// ...
	flag.Parse()
	ctx := context.Background()
	code, err := subcommands.Execute(ctx)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error: ", err)
	}
	os.Exit(int(code))
}

help command incorrectly locates flags in its result

How to reproduce

When, on program foo, a command bar usage includes both a flag (e.g. qux string) and a list of non-flag arguments, (e.g. foo <pid>), run foo help bar

What will happen

The command will display:

foo <pid> -qux string

But running the command that way, e.g. foo 42 -qux meaning will actually pass -qux and meaning as args in addition to 42, instead of interpreting them as flags, so the help is wrong.

Users actually need to run foo -qux meaning 42 instead.

What should have happened

The help should look like:

food -qux string <pid>

Problem cause

Due to the way explain is currently implemented, the only way to have flags be shown correctly in usage is to avoid including the command arguments, which is still wrong.

Suggested solutions

  • at the application level, programs can work around the issue by overriding their commander (resp. subcommands.DefaultCommander) with a custom ExplainCommand function implementing either of the steps proposed below for the subcommands package itself.
  • at the subcommands level, three main solutions exist
    • do not include the flags in explain, leaving it to the user to add them in the cmd.Usage() method
    • insert the flags after the first word in the usage string
    • parse the usage string and only do the insert if the flags are not already present in the usage string.

The first solution is more consistent with the existing package documentation, which explains that "Usage returns a long string explaining the command and giving usage information."

The second form requires less changes from existing users, for whom existing code will suddenly display the correct flags usage string.

The third solution would affect existing code the least but is a bit more complex and may lead to false positives when usage includes other references to the flags in the "long string" it returns, so may cause more errors in the end.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.