-
Notifications
You must be signed in to change notification settings - Fork 0
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
Avoid forking "interface" packages #1
Comments
Hi, thanks for such a quick response to my take at reviving My original intent is to re-implement I may not have enough time today detailing the type incompatibilities I encountered (gotta enjoy the weekend in some other way than writing Go code), but you must have a better understanding than me. Maybe the intrusiveness needed to replicate And after all I'm quite pleased to know the upstream is actually watching and helping, thanks for reaching out and starting the conversation! |
Also note that in commit a5f1ba0 the generated code is not using I plan to introduce some mechanism to automatically sync upstream changes, and to ensure the non-codegen-related packages are never touched. Of course, if deeply-intrusive extensions like |
The reason for the Thus, there are two ways to construct a
The |
I strongly recommend against this since the point of external APIs like those in While I argue against automatic syncs, it's fine to manually cherry pick changes as deemed appropriate. In order for |
I am working on supporting something equivalent to
There are a few internal imports in The It sounds like we will also need I will report back if I get anywhere with this fork-avoiding approach... |
right so it's only the |
I'm still interested in porting @silasdavis Is your code open-source yet? If so may you post a link here so I and other interested users could have a glimpse of it? |
Hi @xen0n, thanks for starting this off. I've just made the repo public here: https://github.com/monax/peptide (not remotely wedded to the name or the repo). It's a bit a rough around the edges as a proof-of-concept, mostly what I've done is stood up a subset of the protobuf-go tests (which pass I have no desire to further fragment efforts so I think your repository should be the home of future efforts if you think my approach can be integrated. Please let me know how I can help and I will endeavour to do so as is compatible with I think maybe I need to go a little further down the implementation rabbit-hole to prove out whether the minimal 🍴 approach is viable (I suspect I may hit more of the issues with the internal API that you have already encountered but we'll see). If we get anywhere I offer my help as co-maintainer, though I would like to take an incremental approach to supporting all of gogoproto, and possibly we can find a way to open up custom code generation for users somehow avoiding the kitchen sink approach (I'm not sure). Some other notes:
|
monax/peptide#1 adds the 3 extensions implemented here and a test file |
The approach I had hoped might work for custom types, namely embedding a There is no way via the // A Converter coverts to/from Go reflect.Value types and protobuf protoreflect.Value types.
type Converter interface {
// PBValueOf converts a reflect.Value to a protoreflect.Value.
PBValueOf(reflect.Value) pref.Value
// GoValueOf converts a protoreflect.Value to a reflect.Value.
GoValueOf(pref.Value) reflect.Value
// IsValidPB returns whether a protoreflect.Value is compatible with this type.
IsValidPB(pref.Value) bool
// IsValidGo returns whether a reflect.Value is compatible with this type.
IsValidGo(reflect.Value) bool
// New returns a new field value.
// For scalars, it returns the default value of the field.
// For composite types, it returns a new mutable value.
New() pref.Value
// Zero returns a new field value.
// For scalars, it returns the default value of the field.
// For composite types, it returns an immutable, empty value.
Zero() pref.Value
} The nice thing about being able to use the // Extension interfaces
// The extend interface is intended to be implemented on a generated type from non-generated code in the same package.
//
// Generators should wrap their existing ProtoReflect() function with a call to ProtoExtend(), like:
//
// func (x *TestMessage) ProtoReflect() protoreflect.Message {
// return x.ProtoExtend(x.protoReflect())
// }
//
// Where `TestMessage` is a generated protobuf message and `protoReflect()` is the 'original' function implementing
// proto.Message.
//
// This allows the default ProtoMessage to be extended without reimplementing the entire interface
type Extend interface {
ProtoExtend(message protoreflect.Message) protoreflect.Message
}
// This struct implements Extend and is intended to be embedded into a generated message type by the generator.
// If other code implements ProtoExtend() directly on the generator struct then that implementation will be run, otherwise
// the behaviour is unchanged from the generated code
type NoopExtend struct {
}
func (_ NoopExtend) ProtoExtend(message protoreflect.Message) protoreflect.Message {
//
return message
} type CustomTypeMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
peptide.NoopExtend
Hash *Hash `protobuf:"bytes,1,opt,name=Hash,proto3" json:"Hash,omitempty"`
}
func (x *CustomTypeMessage) ProtoReflect() protoreflect.Message {
// Call decorator
return x.ProtoExtend(x.protoReflect())
}
func (x *CustomTypeMessage) protoReflect() protoreflect.Message {
mi := &file_cmd_protoc_gen_go_peptide_testdata_gogo_customtype_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
} // Now in non-generated code
func (x *CustomTypeMessage) ProtoExtend(message protoreflect.Message) protoreflect.Message {
// Get access to `protoimpl.MessageState` which does most of the heavy lifting
return &customTypeMessage{message}
}
type customTypeMessage struct {
protoreflect.Message
}
func (c customTypeMessage) ProtoMethods() *protoiface.Methods {
return nil
}
// Override methods selectively
func (c customTypeMessage) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) {
// Alas `messageInfo.initOnce` will panic for custom types because `Converter` is hard coded
c.Message.Range(f)
} If What I have most recently tried therefore is forking I didn't intend to write so much, this obviously ought to be reported up stream to see if the protobuf team might be willing to provide an additional public API. I was thinking that if I could turn up with a working proof of concept using |
Hi, thanks for your interest in possibly upgrading
gogo/protobuf
to work with the new APIs.TL;DR: The
gogogo
project should only consist of a generator and the runtime implementation. There should be no need to fork any of the other packages since a proper implementation of the protobuf reflection API will makegogogo
-generated messages compatible with thegoogle.golang.org/protobuf
module.I recommend not forking anything except:
cmd/protoc-gen-go
(perhaps renamed asprotoc-gen-gogogo
)runtime/protoimpl
(the runtime support machinery for generated message types)Fundamentally,
gogogo
should avoid forkingreflect/protoreflect
andreflect/protodesc
since these are the set of interfaces that defines what a protobuf message is. Ifgogogo
uses a different definition of a protobuf message, thengogogo
doesn't solve the compatibility issues that was afflicting the originalgogo
project.Related, I believe
gogogo
should avoid forkingreflect/protoregistry
or anything undertypes/...
since this leads to gnarly diamond dependency problems. When a.proto
file imports agoogle/protobuf/descriptor.proto
there should only be one obvious Go package path that represents that.proto
file. To avoid ambiguous dependency selection, a given.proto
should only either be generated byprotoc-gen-go
orprotoc-gen-gogogo
, but not both. We should aim for a world where it is theoretically possible for aprotoc-gen-gogogo
-generated package to depend on aprotoc-gen-go
-generated package and vice-versa. The generation of a given package should not care about the exact API representation of any of its dependencies (other than the naming of certain types).The
gogogo
project should not need to fork theproto
,encoding/protojson
, andencoding/prototext
. These packages are implemented in terms of protobuf reflection. If the messages generated byprotoc-gen-gogogo
do not work with these, then it suggests thatgogogo
's implementation of theprotoreflect
interfaces is somehow broken. Theruntime/protoiface
package defines a set of interfaces that a generated message may optionally implement that allows it to have a specialized fast-path implementation (without relying on protobuf reflection). However, the specialized implementation usingprotoiface
should be semantically equivalent to doing so with pure protobuf reflection (but much faster).The text was updated successfully, but these errors were encountered: