Skip to content

Commit

Permalink
Issue spf13#219: added PreRunChain and PostRunChain hooks
Browse files Browse the repository at this point in the history
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
  • Loading branch information
abshivana committed Jan 8, 2016
1 parent 2a426b5 commit 42f7c84
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 2 deletions.
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -641,15 +641,22 @@ command.SetUsageTemplate(s string)

## 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`
- `PreRun`
- `Run`
- `PostRun`
- `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
package main
Expand All @@ -665,6 +672,9 @@ func main() {
var rootCmd = &cobra.Command{
Use: "root [sub]",
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) {
fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args)
},
Expand All @@ -680,11 +690,17 @@ func main() {
PersistentPostRun: func(cmd *cobra.Command, args []string) {
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{
Use: "sub [no options!]",
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) {
fmt.Printf("Inside subCmd PreRun with args: %v\n", args)
},
Expand All @@ -697,6 +713,9 @@ func main() {
PersistentPostRun: func(cmd *cobra.Command, args []string) {
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)
Expand All @@ -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:

* PreRunChainE
* PersistentPreRunE
* PreRunE
* RunE
* PostRunE
* PersistentPostRunE
* PostRunChainE

**Example Usage using RunE:**

Expand Down
61 changes: 61 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,18 @@ type Command struct {
// Silence Usage is an option to silence usage when an error occurs.
SilenceUsage bool
// The *Run functions are executed in the following order:
// * PreRunChain()
// * PersistentPreRun()
// * PreRun()
// * Run()
// * PostRun()
// * PersistentPostRun()
// * PostRunChain()
// 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 func(cmd *Command, args []string)
// PersistentPreRunE: PersistentPreRun but returns an error
Expand All @@ -90,6 +96,10 @@ type Command struct {
PersistentPostRun func(cmd *Command, args []string)
// PersistentPostRunE: PersistentPostRun but returns an 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 bool
// Commands is the list of commands supported by this program.
Expand Down Expand Up @@ -542,6 +552,10 @@ func (c *Command) execute(a []string) (err error) {
c.preRun()
argWoFlags := c.Flags().Args()

if err := c.executePreRunChain(c, argWoFlags); err != nil {
return err
}

for p := c; p != nil; p = p.Parent() {
if p.PersistentPreRunE != nil {
if err := p.PersistentPreRunE(c, argWoFlags); err != nil {
Expand Down Expand Up @@ -587,6 +601,10 @@ func (c *Command) execute(a []string) (err error) {
}
}

if err := c.executePostRunChain(c, argWoFlags); err != nil {
return err
}

return nil
}

Expand All @@ -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 {
s := c.flagErrorBuf.String()

Expand Down

0 comments on commit 42f7c84

Please sign in to comment.