go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/starlark/starlarkproto/doc.go (about) 1 // Copyright 2019 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package starlarkproto exposes protobuf messages as Starlark types. 16 // 17 // Internally a message is stored as a tree of Starlark values, with some type 18 // checking done when manipulating fields, based on proto message descriptors 19 // loaded dynamically from a serialized FileDescriptorSet. 20 // 21 // For example, reading or assigning to a field not defined in a message will 22 // cause a runtime error. Similarly, trying to assign a value of a wrong type to 23 // a non-repeated field will fail. 24 // 25 // # Instantiating messages and default field values 26 // 27 // Each proto message in a loaded package is exposed via a constructor function 28 // that takes optional keyword arguments and produces a new object of *Message 29 // type. 30 // 31 // All unassigned fields are implicitly set to their default zero values on 32 // first access, including message-typed fields, lists and maps. It means, for 33 // example, if a message 'a' has a singular field 'b', that has a field 'c', it 34 // is always fine to write 'a.b.c' to read or set 'c' value, without explicitly 35 // checking that 'b' is set. 36 // 37 // To clear a field, assign None to it (regardless of its type). 38 // 39 // # References and aliasing 40 // 41 // Messages are passed around everywhere by reference. In particular it is 42 // possible to have multiple fields pointing to the exact same message, e.g. 43 // 44 // m1 = M() 45 // m2 = M() 46 // a = A() 47 // m1.f = a 48 // m2.f = a 49 // a.i = 123 # changes both m1.f.i and m2.f.i 50 // 51 // Note that 'm1.f = a' assignment checks the type of 'a', it should either 52 // match type of 'f' identically (no duck typing), or be a dict or None (which 53 // will be converted to messages, see below). 54 // 55 // # Working with repeated fields and maps 56 // 57 // Values of repeated fields are represented by special sequence types that 58 // behave as strongly-typed lists. Think of them as list[T] types or as 59 // hypothetical 'message List<T>' messages. 60 // 61 // When assigning a non-None value R to a repeated field F of type list[T], 62 // the following rules apply (sequentially): 63 // 1. If R is not a sequence => error. 64 // 2. If R has type list[T'], then 65 // a. If T == T', then F becomes an alias of R. 66 // b. If T != T' => error. 67 // 3. A new list[T] is instantiated from R and assigned to F. 68 // 69 // Notice that rule 2 is exactly like the aliasing rule for messages. This is 70 // where "think of them as 'message List<T>'" point of view comes into play. 71 // 72 // As a concrete example, consider this: 73 // 74 // m1 = M() 75 // m1.int64s = [1, 2] # a list is implicitly converted (copied) to list[T] 76 // 77 // m2 = M() 78 // m2.int64s = m1.int64s # points to the exact same list[T] object now 79 // m2.int64s.append(3) # updates both m1 and m2 80 // 81 // m1.int32s = m1.int64s # error! list[int64] is not list[int32] 82 // m1.int32s = list(m1.int64s) # works now (by making a copy of a list copy) 83 // 84 // Maps behave in the similar way. Think of them as strongly-typed map<K,V> 85 // values or as 'message List<Pair<K,V>>' messages. They can be implicitly 86 // instantiated from iterable mappings (e.g. dicts). 87 // 88 // # Auto-conversion of dicts and None's into Messages 89 // 90 // When assigning to a message-typed value (be it a field, an element of a 91 // list[T] or a value of map<K,V>) dicts are implicitly converted into messages 92 // as if via 'T(**d)' call. Similarly, None's are converted into empty messages, 93 // as if via 'T()' call. 94 // 95 // Differences from starlarkproto (beside using different guts): 96 // - Message types are instantiated through proto.new_loader(). 97 // - Text marshaller appends\removes trailing '\n' somewhat differently. 98 // - Text marshaller marshals empty messages as '<>' (without line breaks). 99 // - Bytes fields are represented as str, not as []uint8{...}. 100 // - proto.to_jsonpb doesn't have 'emit_defaults' kwarg (it is always False). 101 // - Better support for proto2 messages. 102 package starlarkproto 103 104 // TODO: support more known types (any, struct). 105 // TODO: delete struct_to_textpb, use dynamic protos instead