mirror of
https://github.com/spf13/cobra
synced 2025-05-05 12:57:22 +00:00
Added the functionality for persistent functions to chain from root to child in the case of PersistentPreRun and from child to root in the case of PersistentPostRun. Added a global internal variable useChainHooks which decides whether to use chain feature, or use the previous logic of nearest defined ancestor. This variable useChainHooks is disabled by default in this commit. Developers importing the cobra package can enable it using the function EnableChainHooks() or disable it using DisableChainHooks(). However, these functions may be called only once externally, preferably in the init of the developers main program.
This commit is contained in:
parent
348f909d8b
commit
76742d6753
2 changed files with 170 additions and 22 deletions
32
README.md
32
README.md
|
@ -641,7 +641,7 @@ 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 `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. These function are run in the following order:
|
||||||
|
|
||||||
- `PersistentPreRun`
|
- `PersistentPreRun`
|
||||||
- `PreRun`
|
- `PreRun`
|
||||||
|
@ -649,7 +649,35 @@ It is possible to run functions before or after the main `Run` function of your
|
||||||
- `PostRun`
|
- `PostRun`
|
||||||
- `PersistentPostRun`
|
- `PersistentPostRun`
|
||||||
|
|
||||||
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`:
|
The `Persistent*Run` functions have two modes of operation:
|
||||||
|
|
||||||
|
1. ChainedMode Enabled
|
||||||
|
- `PersistentPreRun` will run all the defined (non nil) `PersistentPreRun` functions from root to child (both included).
|
||||||
|
- `PersistentPostRun` will run all the defined (non nil) `PersistPostRun` functions from child to root (both included).
|
||||||
|
1. ChainedMode Disabled
|
||||||
|
- `PersistentPreRun` and `PersistentPostRun` will only run the nearest ancestor defined (non nil function) closest to the child. The child is included - meaning, if it defines a Persistent function, only that function is called.
|
||||||
|
|
||||||
|
NOTE: ChainedMode is disabled by default in version 1.0 and will be enabled in version 2.0. In version 3.0 it will cease to exist, and ChainedMode will always be enabled.
|
||||||
|
The ChainedMode can be Enabled or Disabled once at import time as follows
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
import "github.com/spf13/cobra"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if err := cobra.EnableChainHooks(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// cobra.DisableChainHooks() can be used to disable it
|
||||||
|
```
|
||||||
|
The `EnableChainHooks` or `DisableChainHooks` can be called only once at import time. Multiple calls will return errors and have no change in functionality.
|
||||||
|
|
||||||
|
|
||||||
|
An example of two commands which use all of these features is below.
|
||||||
|
In ChainedMode Disabled: When the subcommand is executed, it will run the root command's `PersistentPreRun` but not the root command's `PersistentPostRun`
|
||||||
|
|
||||||
|
In ChainedMode Enabled: When the subcommand is executed, it will run the root command's `PersistentPreRun`, child command's `PersistentPostRun` and root command's `PersistentPostRun`.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
160
command.go
160
command.go
|
@ -26,6 +26,64 @@ import (
|
||||||
flag "github.com/spf13/pflag"
|
flag "github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// useChainHooks determines the behaviour of the Persistent*Run functions.
|
||||||
|
// if true, then PersistentPreRun will run all defined PersistentPreRun
|
||||||
|
// functions from root to child, and PersistentPostRun will
|
||||||
|
// run all defined PersistentPostRun functions from child to root.
|
||||||
|
// if false, it will only run the nearest ancestor of the child that is defined
|
||||||
|
// i.e.if the child has its own Persistent*Run function, only that is executed.
|
||||||
|
// TODO cobra v1 by default keeps useChainHooks as false
|
||||||
|
// TODO in v2, make useChainHooks true
|
||||||
|
// TODO in v3, remove useChainHooks completely and use the logic
|
||||||
|
// as if it were true
|
||||||
|
// Developers may programmatically Enable / Disable this feature only once
|
||||||
|
// at the root level.
|
||||||
|
var useChainHooks bool = false
|
||||||
|
|
||||||
|
// To prevent multiple calls to it.
|
||||||
|
var useChainHooksTouched bool = false
|
||||||
|
|
||||||
|
// EnableChainHooks allows Persistent*Run functions to chain.
|
||||||
|
// Enable/DisableChainHooks can be called only once at the beginning.
|
||||||
|
func EnableChainHooks() error {
|
||||||
|
if useChainHooksTouched {
|
||||||
|
return fmt.Errorf("Toggle useChainHooks can be done only once !")
|
||||||
|
}
|
||||||
|
useChainHooks = true
|
||||||
|
useChainHooksTouched = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableChainHooks makes Persistent*Run functions to call only nearest
|
||||||
|
// ancestor.
|
||||||
|
// Enable/DisableChainHooks can be called only once at the beginning.
|
||||||
|
func DisableChainHooks() error {
|
||||||
|
if useChainHooksTouched {
|
||||||
|
return fmt.Errorf("Toggle useChainHooks can be done only once !")
|
||||||
|
}
|
||||||
|
useChainHooks = false
|
||||||
|
useChainHooksTouched = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
useChainHooksTouched = false
|
||||||
|
// assuming this is cobra v1.0, default behaviour of persistent functions
|
||||||
|
// is to call the nearest ancestor i.e. not to use chain hooks
|
||||||
|
DisableChainHooks()
|
||||||
|
|
||||||
|
// TODO in cobra v2.0, the default behaviour of persistent functions
|
||||||
|
// is to chain i.e. use chain hooks
|
||||||
|
// EnableChainHooks()
|
||||||
|
|
||||||
|
// Allow the user to be able to modify this once at root level.
|
||||||
|
useChainHooksTouched = false
|
||||||
|
|
||||||
|
// TODO in cobra v3.0, remove all signs of useChainHooks and
|
||||||
|
// useChainHooksTouched. Persistent functions should behave as if
|
||||||
|
// useChainHooks is true.
|
||||||
|
}
|
||||||
|
|
||||||
// Command is just that, a command for your application.
|
// Command is just that, a command for your application.
|
||||||
// eg. 'go run' ... 'run' is the command. Cobra requires
|
// eg. 'go run' ... 'run' is the command. Cobra requires
|
||||||
// you to define the usage and description as part of your command
|
// you to define the usage and description as part of your command
|
||||||
|
@ -501,6 +559,43 @@ func (c *Command) Root() *Command {
|
||||||
return findRoot(c)
|
return findRoot(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// execPersistentPreRunFuncs executes the PreRun functions from root to child.
|
||||||
|
// useChainHooks determines whether to run all the functions from root to child
|
||||||
|
// or just the nearest ancestor to the child (included) that has been defined.
|
||||||
|
// TODO to allow migration for existing users - This useChainHooks has been provided
|
||||||
|
// Remove logic when useChainHooks is false in cobra v3.0
|
||||||
|
func (c *Command) execPersistentPreRunFuncs(cmd *Command, argWoFlags []string) error {
|
||||||
|
// TODO remove this once useChainHooks is no longer required
|
||||||
|
if !useChainHooks {
|
||||||
|
for p := cmd; p != nil; p = p.Parent() {
|
||||||
|
if p.PersistentPreRunE != nil {
|
||||||
|
return p.PersistentPreRunE(cmd, argWoFlags)
|
||||||
|
} else if p.PersistentPreRun != nil {
|
||||||
|
p.PersistentPreRun(cmd, argWoFlags)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// always run from root to child
|
||||||
|
if parent := c.Parent(); parent != nil {
|
||||||
|
if err := parent.execPersistentPreRunFuncs(cmd, argWoFlags); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// run the childs PersistentPreRun function
|
||||||
|
var err error = nil
|
||||||
|
switch {
|
||||||
|
case c.PersistentPreRunE != nil:
|
||||||
|
err = c.PersistentPreRunE(cmd, argWoFlags)
|
||||||
|
case c.PersistentPreRun != nil:
|
||||||
|
c.PersistentPreRun(cmd, argWoFlags)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// ArgsLenAtDash will return the length of f.Args at the moment when a -- was
|
// ArgsLenAtDash will return the length of f.Args at the moment when a -- was
|
||||||
// found during arg parsing. This allows your program to know which args were
|
// found during arg parsing. This allows your program to know which args were
|
||||||
// before the -- and which came after. (Description from
|
// before the -- and which came after. (Description from
|
||||||
|
@ -542,17 +637,10 @@ func (c *Command) execute(a []string) (err error) {
|
||||||
c.preRun()
|
c.preRun()
|
||||||
argWoFlags := c.Flags().Args()
|
argWoFlags := c.Flags().Args()
|
||||||
|
|
||||||
for p := c; p != nil; p = p.Parent() {
|
if err := c.execPersistentPreRunFuncs(c, argWoFlags); err != nil {
|
||||||
if p.PersistentPreRunE != nil {
|
return err
|
||||||
if err := p.PersistentPreRunE(c, argWoFlags); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
break
|
|
||||||
} else if p.PersistentPreRun != nil {
|
|
||||||
p.PersistentPreRun(c, argWoFlags)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.PreRunE != nil {
|
if c.PreRunE != nil {
|
||||||
if err := c.PreRunE(c, argWoFlags); err != nil {
|
if err := c.PreRunE(c, argWoFlags); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -575,16 +663,9 @@ func (c *Command) execute(a []string) (err error) {
|
||||||
} else if c.PostRun != nil {
|
} else if c.PostRun != nil {
|
||||||
c.PostRun(c, argWoFlags)
|
c.PostRun(c, argWoFlags)
|
||||||
}
|
}
|
||||||
for p := c; p != nil; p = p.Parent() {
|
|
||||||
if p.PersistentPostRunE != nil {
|
if err := c.execPersistentPostRunFuncs(c, argWoFlags); err != nil {
|
||||||
if err := p.PersistentPostRunE(c, argWoFlags); err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
break
|
|
||||||
} else if p.PersistentPostRun != nil {
|
|
||||||
p.PersistentPostRun(c, argWoFlags)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -596,6 +677,45 @@ func (c *Command) preRun() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// execPersistentPostRunFuncs executes the PostRun functions from child to root.
|
||||||
|
// useChainHooks determines whether to run all the functions from child to root
|
||||||
|
// or just the nearest ancestor to the child (included) that has been defined.
|
||||||
|
// TODO to allow migration for existing users - This useChainHooks has been provided
|
||||||
|
// Remove logic when useChainHooks is false in cobra v3.0
|
||||||
|
func (c *Command) execPersistentPostRunFuncs(cmd *Command, argWoFlags []string) error {
|
||||||
|
// TODO remove this once useChainHooks is no longer required
|
||||||
|
if !useChainHooks {
|
||||||
|
for p := cmd; p != nil; p = p.Parent() {
|
||||||
|
if p.PersistentPostRunE != nil {
|
||||||
|
return p.PersistentPostRunE(cmd, argWoFlags)
|
||||||
|
} else if p.PersistentPostRun != nil {
|
||||||
|
p.PersistentPostRun(cmd, argWoFlags)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// always run from child to root
|
||||||
|
var err error = nil
|
||||||
|
switch {
|
||||||
|
case c.PersistentPostRunE != nil:
|
||||||
|
err = c.PersistentPostRunE(cmd, argWoFlags)
|
||||||
|
case c.PersistentPostRun != nil:
|
||||||
|
c.PersistentPostRun(cmd, argWoFlags)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if parent := c.Parent(); parent != nil {
|
||||||
|
return parent.execPersistentPostRunFuncs(cmd, argWoFlags)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Command) errorMsgFromParse() string {
|
func (c *Command) errorMsgFromParse() string {
|
||||||
s := c.flagErrorBuf.String()
|
s := c.flagErrorBuf.String()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue