New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement SetContext() for passing values between hooks #1118
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -216,6 +216,12 @@ func (c *Command) Context() context.Context { | |
return c.ctx | ||
} | ||
|
||
// SetContext replaces the underlying command context so that parent | ||
// commands can pass down values to their subcommands. | ||
func (c *Command) SetContext(ctx context.Context) { | ||
c.ctx = ctx | ||
} | ||
|
||
// SetArgs sets arguments for the command. It is set to os.Args[1:] by default, if desired, can be overridden | ||
// particularly useful when testing. | ||
func (c *Command) SetArgs(a []string) { | ||
|
@@ -943,9 +949,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { | |
|
||
// We have to pass global context to children command | ||
// if context is present on the parent command. | ||
if cmd.ctx == nil { | ||
cmd.ctx = c.ctx | ||
} | ||
cmd.ctx = c.ctx | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why was the I can imagine, even with the addition of the new This could even be a breaking change if during command execution, the users rely on the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please see #1109 for the motivation behind this change. |
||
|
||
err = cmd.execute(flags) | ||
if err != nil { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -148,6 +148,38 @@ func TestSubcommandExecuteC(t *testing.T) { | |
} | ||
} | ||
|
||
func TestSetContext(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for adding a test case for this!! 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
ctx := context.TODO() | ||
|
||
aKey := "akey" | ||
aVal := "aval" | ||
|
||
parentRun := func(cmd *Command, args []string) { | ||
ctx := context.WithValue(cmd.Context(), aKey, aVal) | ||
cmd.SetContext(ctx) | ||
} | ||
|
||
childRun := func(cmd *Command, args []string) { | ||
if val := cmd.Context().Value(aKey); val != aVal { | ||
t.Errorf(`Context attribute not found in child command. Expected: "%+v". Have: "%+v"`, aVal, val) | ||
} | ||
} | ||
|
||
rootCmd := &Command{Use: "root", Run: parentRun, PersistentPreRun: parentRun} | ||
childCmd := &Command{Use: "child", Run: childRun} | ||
|
||
rootCmd.AddCommand(childCmd) | ||
|
||
if _, err := executeCommandWithContext(ctx, rootCmd, ""); err != nil { | ||
t.Errorf("Root command must not fail: %+v", err) | ||
} | ||
|
||
if _, err := executeCommandWithContext(ctx, rootCmd, "child"); err != nil { | ||
t.Errorf("Subcommand must not fail: %+v", err) | ||
} | ||
|
||
} | ||
|
||
func TestExecuteContext(t *testing.T) { | ||
ctx := context.TODO() | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this any different from
ExecuteContext
? With usingExecuteContext
, a user could pass in the necessary parent context, and then it is executed on the child sub-command.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm not mistaken (it's been a while since this PR was submitted), by using
ExecuteContext
you're not able to set the context in the scope of a command so that nested commands have access to the new, modified context. In other words, the context is only passed to the root command, and then it is simply forwarded without modification to any subcommands. If you want parent commands to be able to act as middleware to their children, you need to have a structure that can be modified within the root context, which might not be desirable. That's whereSetContext
comes in handy. It allows to modify the context at will.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jpmcb Any update? :) Thanks!