Join GitHub today
GitHub is home to over 20 million developers working together to host and review code, manage projects, and build software together.
interfaces: PlugInfo/SlotInfo/ConnectedPlug/ConnectedSlot attribute helpers #4301
Conversation
stolowski
added some commits
Nov 27, 2017
| + } | ||
| + return nil, fmt.Errorf("attribute %q not found", key) | ||
| +} | ||
| + |
zyga
Nov 27, 2017
Contributor
It would be great if you could just test this explicitly in a separate test method.
codecov-io
commented
Nov 27, 2017
•
Codecov Report
@@ Coverage Diff @@
## master #4301 +/- ##
==========================================
+ Coverage 77.91% 77.95% +0.04%
==========================================
Files 446 446
Lines 30820 30841 +21
==========================================
+ Hits 24012 24043 +31
+ Misses 4798 4789 -9
+ Partials 2010 2009 -1
Continue to review full report at Codecov.
|
| @@ -397,6 +397,13 @@ type PlugInfo struct { | ||
| Hooks map[string]*HookInfo | ||
| } | ||
| +func (plug *PlugInfo) Attr(key string) (interface{}, error) { |
bboozzoo
Nov 27, 2017
Contributor
I guess the caller will do a type assert because this is returning interface{}. In such case you may refactor this into
import "reflect"
...
func (plug *PlugInfo) Attr(key string, val interface{}) error {
rt := reflect.TypeOf(val)
if rt.Kind() != reflect.Ptr || val == nil {
return errors.New("not assignable")
}
if v, ok := plug.Attrs[name]; ok {
if reflect.TypeOf(v) != rt.Elem() {
return errors.New("type mismatch")
}
rv := reflect.ValueOf(val)
rv.Elem().Set(reflect.ValueOf(v))
return nil
}
return errors.New("not found")
}
Then the call sites will be cleaner:
var foo string
if err := plug.Attr("foo", &foo); err != nil {
// blah blah failed
}
A working example is right here: https://play.golang.org/p/tyvQ0RpeFA
stolowski
Nov 27, 2017
Contributor
Thanks, I like that, it would simplify the code of interfaces a little bit. @pedronis , @niemeyer your opinion on that? If that's ok I'll update the ConnectedPlug/ConnectedSlot to match.
stolowski
changed the title from
interfaces: small helpers
to
interfaces: PlugInfo/SlotInfo/ConnectedPlug/Connectedslot attribute helpers
Nov 27, 2017
| @@ -45,6 +46,39 @@ type ConnectedSlot struct { | ||
| dynamicAttrs map[string]interface{} | ||
| } | ||
| +// AttrGetter is an interface with Attr getter method common | ||
| +// to ConnectedSlot, ConnectedPlug, PlugInfo and SlotInfo types. | ||
| +type AttrGetter interface { |
niemeyer
Nov 27, 2017
Contributor
This should probably be Attrer or similar. Given typical conventions, it feels like an AttrGet method should exist.
That said, this looks like a left over. There's no usage of the interface in the whole PR, so I suggest just dropping it.
stolowski
Nov 28, 2017
Contributor
Attrer, did you actually mean that? How about AttrReader?
The Attr method name needs to match the method of ConnectedPlug/Slot and PlugInfo/SlotInfo.
This interface is used in the followup branch, I just forgot to update it to match the changes to Attr methods. Fixed now.
pedronis
Dec 5, 2017
Contributor
I think Gustavo did meant Attrer, I can see at least an example where in the stdlib they went for such blunt/ungrammatical naming https://golang.org/pkg/database/sql/driver/#Valuer
| + var v interface{} | ||
| + var ok bool | ||
| + | ||
| + if dynamicAttrs != nil { |
niemeyer
Nov 27, 2017
Contributor
The three lines above can go away and be replaced with just:
v, ok := dynamicAttrs[key]
This will work as you want if the map is nil.
| + if dynamicAttrs != nil { | ||
| + v, ok = dynamicAttrs[key] | ||
| + } | ||
| + if !ok && staticAttrs != nil { |
| + v, ok = staticAttrs[key] | ||
| + } | ||
| + | ||
| + if ok { |
niemeyer
Nov 27, 2017
Contributor
Instead of indenting everything and having to keep in mind what the condition was by the time we get to the last line, it's cleaner to invert the logic and repeat the above statement:
if !ok {
return fmt.Errorf("attribute %q not found", key)
}
(... rest of the logic ...)
niemeyer
Nov 27, 2017
Contributor
This would benefit from context as well. See the related review point.
| + if ok { | ||
| + rt := reflect.TypeOf(val) | ||
| + if rt.Kind() != reflect.Ptr || val == nil { | ||
| + return fmt.Errorf("cannot store the value of attribute %q", key) |
niemeyer
Nov 27, 2017
Contributor
This error message does not reflect the action being done. Neither the developer nor the user at the other end have asked to store an attribute. Should be something along the lines of:
"internal error: cannot get attribute with non-pointer value"
niemeyer
Nov 27, 2017
Contributor
Similarly, we'd benefit from some context. Which interface type, which attribute. Otherwise we'll need to blindly hunt for the error when someone reports this.
| + } | ||
| + | ||
| + if reflect.TypeOf(v) != rt.Elem() { | ||
| + return fmt.Errorf("the type of attribute %q is %s, expected %s", key, reflect.TypeOf(v).Name(), rt) |
niemeyer
Nov 27, 2017
Contributor
Similarly, this message is going to surface to users due to errors from snap publishers. Neither of those need to be Go developers, so we don't want to expose strings such as *map[string]interface{} in an error message.
Also, this is lacking some context that would enable to user or publisher to tell where that attribute is.
Needs to be something similar to:
"snap %q has interface %q with invalid value type for %q attribute"
If you tweak the message further, let's please just preserve the hierarchy in the order of the arguments: snap > interface > attribute.
|
@niemeyer Thanks for the review, I've addressed your points re error messages and code simplifications. |
stolowski
changed the title from
interfaces: PlugInfo/SlotInfo/ConnectedPlug/Connectedslot attribute helpers
to
interfaces: PlugInfo/SlotInfo/ConnectedPlug/ConnectedSlot attribute helpers
Nov 28, 2017
stolowski
referenced this pull request
Nov 28, 2017
Merged
interfaces: use ConnectedPlug/ConnectedSlot types (step 2) #4314
stolowski
referenced this pull request
Dec 5, 2017
Open
interfaces: interface hooks implementation #4358
stolowski
added some commits
Dec 5, 2017
pedronis
approved these changes
Dec 6, 2017
lgtm, wondering about 2 error messages tough
| + } | ||
| + | ||
| + if reflect.TypeOf(v) != rt.Elem() { | ||
| + return fmt.Errorf("snap %q has interface %q with invalid value type for %q attribute", snapName, ifaceName, key) |
pedronis
Dec 6, 2017
Contributor
I find this error a bit too abstract,
"snap %q has interface %q with %q attribute value of invalid type %v, expected %v", snapName, ifaceName, key, reflect.TypeOf(v), rt.Elem()
stolowski
Dec 6, 2017
Contributor
I included type names initially but changed this per earlier request from Gustavo:
"Similarly, this message is going to surface to users due to errors from snap publishers. Neither of those need to be Go developers, so we don't want to expose strings such as *map[string]interface{} in an error message.
Also, this is lacking some context that would enable to user or publisher to tell where that attribute is.
Needs to be something similar to:
"snap %q has interface %q with invalid value type for %q attribute"
pedronis
Dec 6, 2017
Contributor
but 'invalid value type' is not very user friendly either, anyway my worry is that we will miss debug info, maybe we should log the message with types, and surface the one without:
"snap %q has interface %q with %q attribute value with unexpected type"
| + } | ||
| + | ||
| + if reflect.TypeOf(v) != rt.Elem() { | ||
| + return fmt.Errorf("snap %q has interface %q with invalid value type for %q attribute", snapName, ifaceName, key) |
stolowski commentedNov 27, 2017
•
Edited 1 time
-
stolowski
Nov 27, 2017
Small helpers to ease with interfaces refactoring: