-
Notifications
You must be signed in to change notification settings - Fork 1.7k
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
add context.Context to cli.Context #840
Changes from 5 commits
820040e
006fad3
cee005e
1f7d168
98e64f4
2fb0dc1
c8863d0
be6dbba
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 |
---|---|---|
@@ -1,18 +1,22 @@ | ||
package cli | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"flag" | ||
"os" | ||
"os/signal" | ||
"reflect" | ||
"strings" | ||
"syscall" | ||
) | ||
|
||
// Context is a type that is passed through to | ||
// each Handler action in a cli application. Context | ||
// can be used to retrieve context-specific args and | ||
// parsed command-line options. | ||
type Context struct { | ||
context.Context | ||
App *App | ||
Command *Command | ||
shellComplete bool | ||
|
@@ -24,9 +28,21 @@ type Context struct { | |
// NewContext creates a new context. For use in when invoking an App or Command action. | ||
func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { | ||
c := &Context{App: app, flagSet: set, parentContext: parentCtx} | ||
|
||
if parentCtx != nil { | ||
if parentCtx.Context == nil { | ||
parentCtx.Context = context.Background() | ||
} | ||
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. This has a side effect on the parentCtx passed in, which I as a user would find suprising. 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. Also, I think the intended condition in the if was == nil it seems? I think this needs tests, as we can't rely on reviewers to prove this works 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. @AudriusButkevicius this is just to make sure the context is never nil, so that we never panic. Should I remove it and leave the risk up to the user? 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. It's not true, the if statement is wrong and it always overrides it if its NOT nil, and leaves it nil otherwise. I think we should not cause side effects on objects given to us, so if its nil, create a new one locally, and then validate this behaviour with a few tests. 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. @AudriusButkevicius great catch, that was a mistake on my part. I will change != nil to == nil 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. @AudriusButkevicius i also added some propagation tests to ensure the parent context does not get overridden. 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. I'd like to add sigterm tests, but i'm not sure how I can send a sigterm signal to the process, without killing the tests themselves. 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. I still think we should not mutate the parent context.
Then outside the if (oppose to else):
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. @AudriusButkevicius done |
||
c.Context = parentCtx.Context | ||
c.shellComplete = parentCtx.shellComplete | ||
} else { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
go func() { | ||
defer cancel() | ||
sigs := make(chan os.Signal, 1) | ||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) | ||
<-sigs | ||
}() | ||
c.Context = ctx | ||
} | ||
|
||
return c | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
package cli | ||
|
||
import ( | ||
"context" | ||
"flag" | ||
"sort" | ||
"testing" | ||
|
@@ -262,3 +263,24 @@ func TestContext_lookupFlagSet(t *testing.T) { | |
t.Fail() | ||
} | ||
} | ||
|
||
// TestContextPropagation tests that | ||
// *cli.Context always has a valid | ||
// context.Context | ||
func TestContextPropagation(t *testing.T) { | ||
ctx := NewContext(nil, nil, nil) | ||
if ctx.Context == nil { | ||
t.Fatal("expected a non nil context when no parent is present") | ||
} | ||
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. Should be a separate test 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. @AudriusButkevicius I made this into a separate test |
||
parent := NewContext(nil, nil, nil) | ||
parent.Context = context.WithValue(context.Background(), "key", "val") | ||
ctx = NewContext(nil, nil, parent) | ||
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. What about a test where parent.Context is nil, but parent not nil. 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. @AudriusButkevicius i added that scenario to the bottom of this test. |
||
val := ctx.Value("key") | ||
if val == nil { | ||
t.Fatal("expected a parent context to be inherited but got nil") | ||
} | ||
valstr, _ := val.(string) | ||
if valstr != "val" { | ||
t.Fatalf("expected the context value to be %q but got %q", "val", valstr) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module gopkg.in/urfave/cli.v2 | ||
|
||
go 1.12 | ||
marwan-at-work marked this conversation as resolved.
Show resolved
Hide resolved
|
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.
the Go command complained that these were declared and not used
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.
These are not used but they do have side effects/assert evaluation logic so should probably be called?
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.
@AudriusButkevicius the code didn't even compile though, so im not sure how that's supposed to work? In any case, I can just add
var _ = parsedOption
belowThere 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.
Its not the variable declarations, its the calls that assign to the unused variables is what matters.
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.
@AudriusButkevicius again, the code doesn't even compile and tests don't run :)
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.
This is because we're just re-assigning those variables, but not really doing anything with them. It could be that go1.8 (what travis is using) didn't care, but Go 1.13 is smarter.