Skip to content
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

feat(depinject): resolve interface types #12169

Merged
merged 49 commits into from
Jun 9, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
a880bc0
Revert "feat(depinject): key resolvers for interface types (#12103)"
kocubinski Jun 6, 2022
f88a671
feat(depinject): auto resolve interface types
kocubinski Jun 6, 2022
73fd6d1
Include test exercising interface binding.
kocubinski Jun 6, 2022
613592a
Clean up error message
kocubinski Jun 6, 2022
5b2f212
Clean up error message
kocubinski Jun 6, 2022
39e88a7
Typed error message and improve tests
kocubinski Jun 6, 2022
5731a85
Never auto-bind an interface to an interface
kocubinski Jun 7, 2022
427a275
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocubinski/d…
kocubinski Jun 7, 2022
14f03ff
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocubinski/d…
kocubinski Jun 7, 2022
e8d085f
Add Prefer and PreferInModule to depinject API
kocubinski Jun 7, 2022
3ed7c19
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocubinski/d…
kocubinski Jun 7, 2022
0a58684
Fill in docstrings
kocubinski Jun 7, 2022
0468f27
Do not export preference struct
kocubinski Jun 7, 2022
23ea6a7
Add docstrings for errors
kocubinski Jun 7, 2022
295b97c
Rename error
kocubinski Jun 7, 2022
ede3aa8
Rework state for preferred resolvers
kocubinski Jun 7, 2022
d7dd9ad
Refactor preference lookup
kocubinski Jun 8, 2022
4986b0f
Clean up docstrings
kocubinski Jun 8, 2022
b3eeb16
Clean up docstrings
kocubinski Jun 8, 2022
8ce0044
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocubinski/d…
kocubinski Jun 8, 2022
6f2ea2d
Refactor usages of c.resolvers
kocubinski Jun 8, 2022
0f683f4
All is passing with 2 resolver indices
kocubinski Jun 8, 2022
0487894
Remove all but 1 usage of resolversByType
kocubinski Jun 8, 2022
a967121
Better docstrings
kocubinski Jun 8, 2022
5f4321b
Main two indices (for now)
kocubinski Jun 8, 2022
c8e2d2e
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocubinski/d…
kocubinski Jun 8, 2022
05bbeb7
Remove 2nd resolver index
kocubinski Jun 8, 2022
fe1356c
Don't export error constructor fn
kocubinski Jun 8, 2022
445a410
Update docstring
kocubinski Jun 9, 2022
e6735f9
Don't ignore bool in resolverByType call
kocubinski Jun 9, 2022
e57eedf
Remove unnecessary params to provider fn in test
kocubinski Jun 9, 2022
5ef8b41
Fill in comment in test case
kocubinski Jun 9, 2022
6e7d592
Rename field in error struct
kocubinski Jun 9, 2022
4f52a59
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocubinski/d…
kocubinski Jun 9, 2022
de343d4
Improve multiple impl error and fix a failing test in core
kocubinski Jun 9, 2022
9089afb
First pass on interface type resolution docs
kocubinski Jun 9, 2022
ed39682
README formatting
kocubinski Jun 9, 2022
9d47bda
README update
kocubinski Jun 9, 2022
0c4fa6b
Fix link
kocubinski Jun 9, 2022
9e49d79
test(depinject): add preference feature (#12202)
aaronc Jun 9, 2022
70caefc
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocubinski/d…
kocubinski Jun 9, 2022
0f3b4f7
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocubinski/d…
kocubinski Jun 9, 2022
9ba8a47
Rename preference -> interfaceBinding
kocubinski Jun 9, 2022
6dfed89
Remove more usages of preference language
kocubinski Jun 9, 2022
cdb2218
test(depinject): gocuke implementation for prefer feature (#12206)
aaronc Jun 9, 2022
16caa13
Fixes after renaming
kocubinski Jun 9, 2022
0324b41
Revert README changes
kocubinski Jun 9, 2022
0d8f8b8
Fix naming in BDD tests
kocubinski Jun 9, 2022
7141c61
Fix wording
kocubinski Jun 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 35 additions & 33 deletions depinject/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,15 @@ package depinject
import (
"bytes"
"fmt"
"reflect"

"github.com/pkg/errors"

"github.com/cosmos/cosmos-sdk/depinject/internal/graphviz"
"github.com/pkg/errors"
"reflect"
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed

Check notice

Code scanning / CodeQL

Sensitive package import

Certain system packages contain functions which may be a possible source of non-determinism
)

type container struct {
*debugConfig

resolvers map[reflect.Type]resolver
keyedResolvers map[string]resolver
resolvers map[reflect.Type]resolver

moduleKeys map[string]*moduleKey

Expand All @@ -30,12 +27,11 @@ type resolveFrame struct {

func newContainer(cfg *debugConfig) *container {
return &container{
debugConfig: cfg,
resolvers: map[reflect.Type]resolver{},
keyedResolvers: map[string]resolver{},
moduleKeys: map[string]*moduleKey{},
callerStack: nil,
callerMap: map[Location]bool{},
debugConfig: cfg,
resolvers: map[reflect.Type]resolver{},
moduleKeys: map[string]*moduleKey{},
callerStack: nil,
callerMap: map[Location]bool{},
}
}

Expand Down Expand Up @@ -78,13 +74,8 @@ func (c *container) call(provider *ProviderDescriptor, moduleKey *moduleKey) ([]
return out, nil
}

func (c *container) getResolver(typ reflect.Type, key string) (resolver, error) {
if key != "" {
if vr, ok := c.keyedResolvers[key]; ok {
return vr, nil
}
}

func (c *container) getResolver(typ reflect.Type) (resolver, error) {
c.logf("Resolving %v", typ)
if vr, ok := c.resolvers[typ]; ok {
return vr, nil
}
Expand Down Expand Up @@ -130,7 +121,27 @@ func (c *container) getResolver(typ reflect.Type, key string) (resolver, error)
c.resolvers[mapType] = &mapOfOnePerModuleResolver{r}
}

return c.resolvers[typ], nil
res := c.resolvers[typ]

if res == nil && typ.Kind() == reflect.Interface {
var found bool
for k, r := range c.resolvers {
if k.Kind() != reflect.Interface && k.Implements(typ) {
c.logf("Found candidate resolver, implicitly binding interface %v to implementing type %v.", typ, k)
if found {
return nil, &ErrMultipleImplicitInterfaceBindings{Interface: typ}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should return a list of all candidate matches here

}
res = r
found = true
}
}
Fixed Show fixed Hide fixed

if found {
c.resolvers[typ] = res
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should cache the binding in resolvers


return res, nil
}

var stringType = reflect.TypeOf("")
Expand All @@ -155,7 +166,7 @@ func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (inter
return nil, fmt.Errorf("one-per-module type %v can't be used as an input parameter", typ)
}

vr, err := c.getResolver(typ, in.Key)
vr, err := c.getResolver(typ)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -197,7 +208,7 @@ func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (inter
typ = typ.Elem()
}

vr, err := c.getResolver(typ, out.Key)
vr, err := c.getResolver(typ)
if err != nil {
return nil, err
}
Expand All @@ -219,10 +230,6 @@ func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (inter
idxInValues: i,
}
c.resolvers[typ] = vr

if out.Key != "" {
c.keyedResolvers[out.Key] = vr
}
}

c.addGraphEdge(providerGraphNode, vr.typeGraphNode())
Expand Down Expand Up @@ -257,18 +264,13 @@ func (c *container) addNode(provider *ProviderDescriptor, key *moduleKey) (inter
}

typeGraphNode := c.typeGraphNode(typ)
mdr := &moduleDepResolver{
c.resolvers[typ] = &moduleDepResolver{
typ: typ,
idxInValues: i,
node: node,
valueMap: map[*moduleKey]reflect.Value{},
graphNode: typeGraphNode,
}
c.resolvers[typ] = mdr

if out.Key != "" {
c.keyedResolvers[out.Key] = mdr
}

c.addGraphEdge(providerGraphNode, typeGraphNode)
}
Expand Down Expand Up @@ -321,7 +323,7 @@ func (c *container) resolve(in ProviderInput, moduleKey *moduleKey, caller Locat
return reflect.ValueOf(OwnModuleKey{moduleKey}), nil
}

vr, err := c.getResolver(in.Type, in.Key)
vr, err := c.getResolver(in.Type)
if err != nil {
return reflect.Value{}, err
}
Expand Down
34 changes: 18 additions & 16 deletions depinject/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -644,35 +644,37 @@ type AlsoDuck interface {
}

type Mallard struct{}
type Canvasback struct{}

func (duck Mallard) quack() {}

type KeyedOutput struct {
depinject.Out
Duck Duck `key:"foo"`
}

type KeyedInput struct {
depinject.In
AlsoDuck AlsoDuck `key:"foo"`
}
func (duck Mallard) quack() {}
func (duck Canvasback) quack() {}

type Pond struct {
Duck AlsoDuck
}

func TestKeyedInputOutput(t *testing.T) {
func TestImplicitBindings(t *testing.T) {
var pond Pond

require.NoError(t,
depinject.Inject(
depinject.Provide(
func() KeyedOutput { return KeyedOutput{Duck: Mallard{}} },
func(in KeyedInput) Pond {
require.NotNil(t, in.AlsoDuck)
return Pond{Duck: in.AlsoDuck}
func() Mallard { return Mallard{} },
func(duck Duck) Pond {
require.NotNil(t, duck)
return Pond{Duck: duck}
}),
&pond))

require.NotNil(t, pond)

require.Error(t,
depinject.Inject(
depinject.Provide(
func() Mallard { return Mallard{} },
func() Canvasback { return Canvasback{} },
func(duck Duck) Pond {
return Pond{Duck: duck}
}),
&pond))
}
10 changes: 10 additions & 0 deletions depinject/errors.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
package depinject

import (
"fmt"
"reflect"

"github.com/pkg/errors"
)

type ErrMultipleImplicitInterfaceBindings struct {
Interface reflect.Type
Err error
}

func (err ErrMultipleImplicitInterfaceBindings) Error() string {
return fmt.Sprintf("Multiple implementations found for interface %v", err.Interface)
}

func duplicateDefinitionError(typ reflect.Type, duplicateLoc Location, existingLoc string) error {
return errors.Errorf("duplicate provision of type %v by %s\n\talready provided by %s",
typ, duplicateLoc, existingLoc)
Expand Down
2 changes: 0 additions & 2 deletions depinject/provider_desc.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,10 @@ type ProviderDescriptor struct {
type ProviderInput struct {
Type reflect.Type
Optional bool
Key string
}

type ProviderOutput struct {
Type reflect.Type
Key string
}

func ExtractProviderDescriptor(provider interface{}) (ProviderDescriptor, error) {
Expand Down
24 changes: 0 additions & 24 deletions depinject/provider_desc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,6 @@ type StructOut struct {
Y []byte
}

type KeyedIn struct {
depinject.In
X string `key:"theKey"`
}

type KeyedOut struct {
depinject.Out
X string `key:"theKey"`
}

func TestExtractProviderDescriptor(t *testing.T) {
var (
intType = reflect.TypeOf(0)
Expand Down Expand Up @@ -97,20 +87,6 @@ func TestExtractProviderDescriptor(t *testing.T) {
nil,
true,
},
{
name: "keyed input",
ctr: func(_ KeyedIn) int { return 0 },
wantIn: []depinject.ProviderInput{{Type: stringType, Key: "theKey"}},
wantOut: []depinject.ProviderOutput{{Type: intType}},
wantErr: false,
},
{
name: "keyed output",
ctr: func(s string) KeyedOut { return KeyedOut{X: "foo"} },
wantIn: []depinject.ProviderInput{{Type: stringType}},
wantOut: []depinject.ProviderOutput{{Type: stringType, Key: "theKey"}},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
14 changes: 0 additions & 14 deletions depinject/struct_args.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,9 @@ func structArgsInTypes(typ reflect.Type) ([]ProviderInput, error) {
}
}

var key string
keyTag, keyFound := f.Tag.Lookup("key")
if keyFound {
key = keyTag
}

res = append(res, ProviderInput{
Type: f.Type,
Optional: optional,
Key: key,
})
}
return res, nil
Expand Down Expand Up @@ -158,15 +151,8 @@ func structArgsOutTypes(typ reflect.Type) []ProviderOutput {
continue
}

var key string
keyTag, keyFound := f.Tag.Lookup("key")
if keyFound {
key = keyTag
}

res = append(res, ProviderOutput{
Type: f.Type,
Key: key,
})
}
return res
Expand Down
2 changes: 1 addition & 1 deletion x/auth/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ func provideModuleBasic() runtime.AppModuleBasicWrapper {
type authOutputs struct {
depinject.Out

AccountKeeper keeper.AccountKeeper `key:"cosmos.auth.v1.AccountKeeper"`
AccountKeeper keeper.AccountKeeper
Module runtime.AppModuleWrapper
}

Expand Down
4 changes: 2 additions & 2 deletions x/bank/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ type bankInputs struct {
depinject.In

Config *modulev1.Module
AccountKeeper types.AccountKeeper `key:"cosmos.auth.v1.AccountKeeper"`
AccountKeeper types.AccountKeeper
Cdc codec.Codec
Subspace paramtypes.Subspace
Key *store.KVStoreKey
Expand All @@ -230,7 +230,7 @@ type bankInputs struct {
type bankOutputs struct {
depinject.Out

BankKeeper keeper.Keeper `key:"cosmos.bank.v1.Keeper"`
BankKeeper keeper.BaseKeeper
Module runtime.AppModuleWrapper
}

Expand Down
4 changes: 2 additions & 2 deletions x/feegrant/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ type feegrantInputs struct {

Key *store.KVStoreKey
Cdc codec.Codec
AccountKeeper feegrant.AccountKeeper `key:"cosmos.auth.v1.AccountKeeper"`
BankKeeper feegrant.BankKeeper `key:"cosmos.bank.v1.Keeper"`
AccountKeeper feegrant.AccountKeeper
BankKeeper feegrant.BankKeeper
Registry cdctypes.InterfaceRegistry
}

Expand Down
6 changes: 3 additions & 3 deletions x/staking/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@ type stakingInputs struct {
depinject.In

Config *modulev1.Module
AccountKeeper types.AccountKeeper `key:"cosmos.auth.v1.AccountKeeper"`
BankKeeper types.BankKeeper `key:"cosmos.bank.v1.Keeper"`
AccountKeeper types.AccountKeeper
BankKeeper types.BankKeeper
Cdc codec.Codec
Subspace paramstypes.Subspace
Key *store.KVStoreKey
Expand All @@ -208,7 +208,7 @@ type stakingInputs struct {
type stakingOutputs struct {
depinject.Out

StakingKeeper *keeper.Keeper `key:"cosmos.staking.v1.Keeper"`
StakingKeeper *keeper.Keeper
Module runtime.AppModuleWrapper
}

Expand Down