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