Issue #219: added PreRunChain and PostRunChain hooks

PreRunChain functions run in the order of root to child.
PostRunChain functions run in the order of child to root.

The overall exection order for a command is
PreRunChain
PersistentPreRun
PreRun
Run
PostRun
PersistentPostRun
PostRunChain

The following gist describes the usage and functionality
https://gist.github.com/algrebe/23cc8bf4739a129a6d0d
This commit is contained in:
Anthony Rebello 2016-01-08 12:42:10 +05:30
parent 2a426b5c59
commit 3dbb5be47c
2 changed files with 84 additions and 2 deletions

View file

@ -641,15 +641,22 @@ command.SetUsageTemplate(s string)
## PreRun or PostRun Hooks ## PreRun or PostRun Hooks
It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherrited by children if they do not declare their own. These function are run in the following order: It is possible to run functions before or after the main `Run` function of your command. The `PreRunChain`, `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun`, `PostRun` and `PostRunChain` will be executed after `Run`. The `Persistent*Run` functions will be inherrited by children if they do not declare their own. These function are run in the following order:
- `PreRunChain`
- `PersistentPreRun` - `PersistentPreRun`
- `PreRun` - `PreRun`
- `Run` - `Run`
- `PostRun` - `PostRun`
- `PersistentPostRun` - `PersistentPostRun`
- `PostRunChain`
An example of two commands which use all of these features is below. When the subcommand is executed, it will run the root command's `PersistentPreRun` but not the root command's `PersistentPostRun`:
`PreRunChain` executes all PreRunChain functions from the root to the child. `PostRunChain` executes all PostRunChain functions from the child to the root. These
are useful in cases such as setting the logger and profiling at the root level in `PreRunChain`, and stopping the profiler once the proram finishes in the `PostRunChain`, while still allowing subcommand `PreRunChain` and `PostRunChain` commands to execute for their children.
An example of two commands which use all of these features is below. When the subcommand is executed, it will run the root command's `PersistentPreRun` but not the root command's `PersistentPostRun` - because the subcommand has overridden the root command's `PersistentPostRun`. However, it will still run the root command's `PreRunChain` and `PostRunChain` in addition to the subcommand's `PreRunChain` and `PostRunChain`, because chains are supposed to execute all defined
chain functions, and not just the overridden one.
```go ```go
package main package main
@ -665,6 +672,9 @@ func main() {
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
Use: "root [sub]", Use: "root [sub]",
Short: "My root command", Short: "My root command",
PreRunChain: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PreRunChain with args: %v\n", args)
},
PersistentPreRun: func(cmd *cobra.Command, args []string) { PersistentPreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args) fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args)
}, },
@ -680,11 +690,17 @@ func main() {
PersistentPostRun: func(cmd *cobra.Command, args []string) { PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args) fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args)
}, },
PostRunChain: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside rootCmd PostRunChain with args: %v\n", args)
},
} }
var subCmd = &cobra.Command{ var subCmd = &cobra.Command{
Use: "sub [no options!]", Use: "sub [no options!]",
Short: "My subcommand", Short: "My subcommand",
PreRunChain: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside subCmd PreRunChain with args: %v\n", args)
},
PreRun: func(cmd *cobra.Command, args []string) { PreRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside subCmd PreRun with args: %v\n", args) fmt.Printf("Inside subCmd PreRun with args: %v\n", args)
}, },
@ -697,6 +713,9 @@ func main() {
PersistentPostRun: func(cmd *cobra.Command, args []string) { PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside subCmd PersistentPostRun with args: %v\n", args) fmt.Printf("Inside subCmd PersistentPostRun with args: %v\n", args)
}, },
PostRunChain: func(cmd *cobra.Command, args []string) {
fmt.Printf("Inside subCmd PostRunChain with args: %v\n", args)
},
} }
rootCmd.AddCommand(subCmd) rootCmd.AddCommand(subCmd)
@ -714,11 +733,13 @@ func main() {
Cobra also has functions where the return signature is an error. This allows for errors to bubble up to the top, providing a way to handle the errors in one location. The current list of functions that return an error is: Cobra also has functions where the return signature is an error. This allows for errors to bubble up to the top, providing a way to handle the errors in one location. The current list of functions that return an error is:
* PreRunChainE
* PersistentPreRunE * PersistentPreRunE
* PreRunE * PreRunE
* RunE * RunE
* PostRunE * PostRunE
* PersistentPostRunE * PersistentPostRunE
* PostRunChainE
**Example Usage using RunE:** **Example Usage using RunE:**

View file

@ -64,12 +64,18 @@ type Command struct {
// Silence Usage is an option to silence usage when an error occurs. // Silence Usage is an option to silence usage when an error occurs.
SilenceUsage bool SilenceUsage bool
// The *Run functions are executed in the following order: // The *Run functions are executed in the following order:
// * PreRunChain()
// * PersistentPreRun() // * PersistentPreRun()
// * PreRun() // * PreRun()
// * Run() // * Run()
// * PostRun() // * PostRun()
// * PersistentPostRun() // * PersistentPostRun()
// * PostRunChain()
// All functions get the same args, the arguments after the command name // All functions get the same args, the arguments after the command name
// PreRunChain: runs all PreRunChain functions from root to child sequentially
PreRunChain func(cmd *Command, args []string)
// PreRunChainE: PreRunChain but returns an error
PreRunChainE func(cmd *Command, args []string) error
// PersistentPreRun: children of this command will inherit and execute // PersistentPreRun: children of this command will inherit and execute
PersistentPreRun func(cmd *Command, args []string) PersistentPreRun func(cmd *Command, args []string)
// PersistentPreRunE: PersistentPreRun but returns an error // PersistentPreRunE: PersistentPreRun but returns an error
@ -90,6 +96,10 @@ type Command struct {
PersistentPostRun func(cmd *Command, args []string) PersistentPostRun func(cmd *Command, args []string)
// PersistentPostRunE: PersistentPostRun but returns an error // PersistentPostRunE: PersistentPostRun but returns an error
PersistentPostRunE func(cmd *Command, args []string) error PersistentPostRunE func(cmd *Command, args []string) error
// PostRunChain: runs all PostRunChain functions from child to root sequentially
PostRunChain func(cmd *Command, args []string)
// PostRunChainE: PostRunChain but returns an error
PostRunChainE func(cmd *Command, args []string) error
// DisableAutoGenTag remove // DisableAutoGenTag remove
DisableAutoGenTag bool DisableAutoGenTag bool
// Commands is the list of commands supported by this program. // Commands is the list of commands supported by this program.
@ -542,6 +552,10 @@ func (c *Command) execute(a []string) (err error) {
c.preRun() c.preRun()
argWoFlags := c.Flags().Args() argWoFlags := c.Flags().Args()
if err := c.executePreRunChain(c, argWoFlags); err != nil {
return err
}
for p := c; p != nil; p = p.Parent() { for p := c; p != nil; p = p.Parent() {
if p.PersistentPreRunE != nil { if p.PersistentPreRunE != nil {
if err := p.PersistentPreRunE(c, argWoFlags); err != nil { if err := p.PersistentPreRunE(c, argWoFlags); err != nil {
@ -587,6 +601,10 @@ func (c *Command) execute(a []string) (err error) {
} }
} }
if err := c.executePostRunChain(c, argWoFlags); err != nil {
return err
}
return nil return nil
} }
@ -596,6 +614,49 @@ func (c *Command) preRun() {
} }
} }
// executePreRunChain executes all the PreRunChain functions from
// the root to the child.
func (c *Command) executePreRunChain(cmd *Command, args []string) error {
parent := c.Parent()
if parent != nil {
if err := parent.executePreRunChain(cmd, args); err != nil {
return err
}
}
var err error = nil
switch {
case c.PreRunChainE != nil:
err = c.PreRunChainE(cmd, args)
case c.PreRunChain != nil:
c.PreRunChain(cmd, args)
}
return err
}
// executePostRunChain executes all the PostRunChain functions from
// the child to the root.
func (c *Command) executePostRunChain(cmd *Command, args []string) error {
var err error = nil
switch {
case c.PostRunChainE != nil:
err = c.PostRunChainE(cmd, args)
case c.PostRunChain != nil:
c.PostRunChain(cmd, args)
}
if err != nil {
return err
}
parent := c.Parent()
if parent != nil {
return parent.executePostRunChain(cmd, args)
}
return nil
}
func (c *Command) errorMsgFromParse() string { func (c *Command) errorMsgFromParse() string {
s := c.flagErrorBuf.String() s := c.flagErrorBuf.String()