diff --git a/content/best-practices/dos-donts.md b/content/best-practices/dos-donts.md index 024deed3..3fc74d91 100644 --- a/content/best-practices/dos-donts.md +++ b/content/best-practices/dos-donts.md @@ -168,6 +168,11 @@ when a perfectly suitable common type already exists! * [`color`](https://github.com/googleapis/googleapis/blob/master/google/type/color.proto) is a color in the RGBA color space. +**Note:** While the "Well-Known Types" (such as `Duration` and `Timestamp`) are +included with the Protocol Buffers compiler, the "Common Types" (such as `Date` +and `Money`) are not. To use the Common Types, you may need to add a dependency +on the [googleapis repository](https://github.com/googleapis/googleapis). + ## **Do** Define Message Types in Separate Files {#separate-files} diff --git a/content/editions/features.md b/content/editions/features.md index 5d3fe5ea..2764f3e1 100644 --- a/content/editions/features.md +++ b/content/editions/features.md @@ -78,12 +78,10 @@ message Corge { } ``` -<<<<<<< HEAD In this example, the setting "`GRAULT"` in the lowest-level scope feature definition overrides the non-nested-scope "`QUUX`" setting. And within the Garply message, "`WALDO`" overrides "`QUUX`." -<<<<<<< HEAD ### `features.default_symbol_visibility` {#symbol-vis} This feature enables setting the default visibility for messages and enums, @@ -159,7 +157,7 @@ message ImportedMessage { } ``` -### `features.enforce_naming_style` {#enforce-naming} +### `features.enforce_naming_style` {#enforce_naming} Introduced in Edition 2024, this feature enables strict naming style enforcement as defined in @@ -201,7 +199,7 @@ message Foo { } ``` -Edition 2025 defaults to `STYLE2024`, so an override is needed to keep the +Edition 2024 defaults to `STYLE2024`, so an override is needed to keep the non-conformant field name: ```proto @@ -214,149 +212,7 @@ message Foo { int64 bar_1 = 1; } ``` -||||||| parent of dcf50a2 (This documentation change includes the following:) -In this example, the setting `GRAULT` in the field-scope feature definition -overrides the message-scope QUUX setting. -======= -In this example, the setting "`GRAULT"` in the lowest-level scope feature -definition overrides the non-nested-scope "`QUUX`" setting. And within the -Garply message, "`WALDO`" overrides "`QUUX`." ->>>>>>> dcf50a2 (This documentation change includes the following:) - -||||||| parent of 81fd217 (This documentation change includes the following:) -======= -### `features.default_symbol_visibility` {#symbol-vis} - -This feature enables setting the default visibility for messages and enums, -making them available or unavailable when imported by other protos. Use of this -feature will reduce dead symbols in order to create smaller binaries. - -In addition to setting the defaults for the entire file, you can use the `local` -and `export` keywords to set per-field behavior. Read more about this at -[`export` / `local` Keywords](/editions/overview#export-local). - -**Values available:** - -* `EXPORT_ALL`: This is the default prior to Edition 2024. All messages and - enums are exported by default. -* `EXPORT_TOP_LEVEL`: All top-level symbols default to export; nested default - to local. -* `LOCAL_ALL`: All symbols default to local. -* `STRICT`: All symbols local by default. Nested types cannot be exported, - except for a special-case caveat for message `{ enum {} reserved 1 to max; - }`. This is the recommended setting for new protos. - -**Applicable to the following scope:** Enum, Message - -**Added in:** Edition 2024 - -**Default behavior per syntax/edition:** - -Syntax/edition | Default --------------- | ------------------ -2024 | `EXPORT_TOP_LEVEL` -2023 | `EXPORT_ALL` -proto3 | `EXPORT_ALL` -proto2 | `EXPORT_ALL` - -**Note:** Feature settings on different schema elements -[have different scopes](#cascading). - -The following sample shows how you can apply the feature to elements in your -proto schema definition files: - -```proto -// foo.proto -edition = "2024"; - -// Symbol visibility defaults to EXPORT_TOP_LEVEL. Setting -// default_symbol_visibility overrides these defaults -option features.default_symbol_visibility = LOCAL_ALL; - -// Top-level symbols are exported by default in Edition 2024; applying the local -// keyword overrides this -export message LocalMessage { - int32 baz = 1; - // Nested symbols are local by default in Edition 2024; applying the export - // keyword overrides this - enum ExportedNestedEnum { - UNKNOWN_EXPORTED_NESTED_ENUM_VALUE = 0; - } -} - -// bar.proto -edition = "2024"; - -import "foo.proto"; - -message ImportedMessage { - // The following is valid because the imported message explicitly overrides - // the visibility setting in foo.proto - LocalMessage bar = 1; - - // The following is not valid because default_symbol_visibility is set to - // `LOCAL_ALL` - // LocalMessage.ExportedNestedEnum qux = 2; -} -``` - -### `features.enforce_naming_style` {#enforce-naming} - -Introduced in Edition 2024, this feature enables strict naming style enforcement -as defined in -[the style guide](/programming-guides/style) to ensure -protos are round-trippable by default with a feature value to opt-out to use - -**Values available:** - -* `STYLE2024`: Enforces strict adherence to the style guide for naming. -* `STYLE_LEGACY`: Applies the pre-Edition 2024 level of style guide - enforcement. - -**Applicable to the following scope:** File - -**Added in:** 2024 - -**Default behavior per syntax/edition:** - -Syntax/edition | Default --------------- | -------------- -2024 | `STYLE2024` -2023 | `STYLE_LEGACY` -proto3 | `STYLE_LEGACY` -proto2 | `STYLE_LEGACY` - -**Note:** Feature settings on different schema elements -[have different scopes](#cascading). - -The following code sample shows an Edition 2023 file: - -Edition 2023 defaults to `STYLE_LEGACY`, so a non-conformant field name is fine: - -```proto -edition = "2023"; - -message Foo { - // A non-conforming field name is not a problem - int64 bar_1 = 1; -} -``` -Edition 2025 defaults to `STYLE2024`, so an override is needed to keep the -non-conformant field name: - -```proto -edition = "2024"; - -// To keep the non-conformant field name, override the STYLE2024 setting -option features.enforce_naming_style = "STYLE_LEGACY"; - -message Foo { - int64 bar_1 = 1; -} -``` - ->>>>>>> 81fd217 (This documentation change includes the following:) ### `features.enum_type` {#enum_type} This feature sets the behavior for how enum values that aren't contained within @@ -379,26 +235,12 @@ and after of a proto3 file. **Default behavior per syntax/edition:** -<<<<<<< HEAD -Syntax/edition | Default --------------- | -------- -2024 | `OPEN` -2023 | `OPEN` -proto3 | `OPEN` -proto2 | `CLOSED` - -**Note:** Feature settings on different schema elements -[have different scopes](#cascading). -||||||| parent of 81fd217 (This documentation change includes the following:) -**Behavior in proto3:** `OPEN` -======= Syntax/edition | Default -------------- | -------- 2024 | `OPEN` 2023 | `OPEN` proto3 | `OPEN` proto2 | `CLOSED` ->>>>>>> 81fd217 (This documentation change includes the following:) **Note:** Feature settings on different schema elements [have different scopes](#cascading). @@ -450,19 +292,7 @@ whether a protobuf field has a value. **Applicable to the following scopes:** File, Field -<<<<<<< HEAD -<<<<<<< HEAD -**Added in:** 2023 -||||||| parent of dcf50a2 (This documentation change includes the following:) -**Default value in the Edition 2023:** `EXPLICIT` -======= -**Default behavior in the Edition 2023:** `EXPLICIT` ->>>>>>> dcf50a2 (This documentation change includes the following:) -||||||| parent of 81fd217 (This documentation change includes the following:) -**Default behavior in the Edition 2023:** `EXPLICIT` -======= **Added in:** 2023 ->>>>>>> 81fd217 (This documentation change includes the following:) **Default behavior per syntax/edition:** @@ -521,21 +351,8 @@ message Bar { After running Prototiller, the equivalent code might look like this: ```proto -<<<<<<< HEAD -<<<<<<< HEAD edition = "2024"; // Setting the file-level field_presence feature matches the proto3 implicit default -||||||| parent of dcf50a2 (This documentation change includes the following:) -edition = "2023"; -======= -edition = "2023"; -||||||| parent of 81fd217 (This documentation change includes the following:) -edition = "2023"; -======= -edition = "2024"; ->>>>>>> 81fd217 (This documentation change includes the following:) -// Setting the file-level field_presence feature matches the proto3 implicit default ->>>>>>> dcf50a2 (This documentation change includes the following:) option features.field_presence = IMPLICIT; message Bar { @@ -572,26 +389,12 @@ and after of a proto3 file. Editions behavior matches the behavior in proto3. **Default behavior per syntax/edition:** -<<<<<<< HEAD -Syntax/edition | Default --------------- | -------------------- -2024 | `ALLOW` -2023 | `ALLOW` -proto3 | `ALLOW` -proto2 | `LEGACY_BEST_EFFORT` - -**Note:** Feature settings on different schema elements -[have different scopes](#cascading). -||||||| parent of 81fd217 (This documentation change includes the following:) -**Behavior in proto3:** `ALLOW` -======= Syntax/edition | Default -------------- | -------------------- 2024 | `ALLOW` 2023 | `ALLOW` proto3 | `ALLOW` proto2 | `LEGACY_BEST_EFFORT` ->>>>>>> 81fd217 (This documentation change includes the following:) **Note:** Feature settings on different schema elements [have different scopes](#cascading). @@ -611,22 +414,8 @@ message Foo { After running Prototiller, the equivalent code might look like this: ```proto -<<<<<<< HEAD -<<<<<<< HEAD edition = "2024"; option features.json_format = LEGACY_BEST_EFFORT; -||||||| parent of dcf50a2 (This documentation change includes the following:) -edition = "2023"; -features.json_format = LEGACY_BEST_EFFORT; -======= -edition = "2023"; -||||||| parent of 81fd217 (This documentation change includes the following:) -edition = "2023"; -======= -edition = "2024"; ->>>>>>> 81fd217 (This documentation change includes the following:) -option features.json_format = LEGACY_BEST_EFFORT; ->>>>>>> dcf50a2 (This documentation change includes the following:) message Foo { string bar = 1; @@ -663,26 +452,12 @@ the following conditions are met: **Default behavior per syntax/edition:** -<<<<<<< HEAD -Syntax/edition | Default --------------- | ----------------- -2024 | `LENGTH_PREFIXED` -2023 | `LENGTH_PREFIXED` -proto3 | `LENGTH_PREFIXED` -proto2 | `LENGTH_PREFIXED` - -**Note:** Feature settings on different schema elements -[have different scopes](#cascading). -||||||| parent of 81fd217 (This documentation change includes the following:) -**Behavior in proto3:** `LENGTH_PREFIXED`. Proto3 doesn't support `DELIMITED`. -======= Syntax/edition | Default -------------- | ----------------- 2024 | `LENGTH_PREFIXED` 2023 | `LENGTH_PREFIXED` proto3 | `LENGTH_PREFIXED` proto2 | `LENGTH_PREFIXED` ->>>>>>> 81fd217 (This documentation change includes the following:) **Note:** Feature settings on different schema elements [have different scopes](#cascading). @@ -733,26 +508,12 @@ for `repeated` fields has been migrated to in Editions. **Default behavior per syntax/edition:** -<<<<<<< HEAD -Syntax/edition | Default --------------- | ---------- -2024 | `PACKED` -2023 | `PACKED` -proto3 | `PACKED` -proto2 | `EXPANDED` - -**Note:** Feature settings on different schema elements -[have different scopes](#cascading). -||||||| parent of 81fd217 (This documentation change includes the following:) -**Behavior in proto3:** `PACKED` -======= Syntax/edition | Default -------------- | ---------- 2024 | `PACKED` 2023 | `PACKED` proto3 | `PACKED` proto2 | `EXPANDED` ->>>>>>> 81fd217 (This documentation change includes the following:) **Note:** Feature settings on different schema elements [have different scopes](#cascading). @@ -826,7 +587,6 @@ and after of a proto3 file. **Default behavior per syntax/edition:** -<<<<<<< HEAD Syntax/edition | Default -------------- | -------- 2024 | `VERIFY` @@ -834,19 +594,6 @@ Syntax/edition | Default proto3 | `VERIFY` proto2 | `NONE` -**Note:** Feature settings on different schema elements -[have different scopes](#cascading). -||||||| parent of 81fd217 (This documentation change includes the following:) -**Behavior in proto3:** `VERIFY` -======= -Syntax/edition | Default --------------- | -------- -2024 | `VERIFY` -2023 | `VERIFY` -proto3 | `VERIFY` -proto2 | `NONE` ->>>>>>> 81fd217 (This documentation change includes the following:) - **Note:** Feature settings on different schema elements [have different scopes](#cascading). @@ -1150,26 +897,12 @@ before and after of a proto3 file. **Default behavior per syntax/edition:** -<<<<<<< HEAD -Syntax/edition | Default --------------- | --------- -2024 | `DEFAULT` -2023 | `DEFAULT` -proto3 | `DEFAULT` -proto2 | `DEFAULT` - -**Note:** Feature settings on different schema elements -[have different scopes](#cascading). -||||||| parent of 81fd217 (This documentation change includes the following:) -**Behavior in proto3:** `DEFAULT` -======= Syntax/edition | Default -------------- | --------- 2024 | `DEFAULT` 2023 | `DEFAULT` proto3 | `DEFAULT` proto2 | `DEFAULT` ->>>>>>> 81fd217 (This documentation change includes the following:) **Note:** Feature settings on different schema elements [have different scopes](#cascading). @@ -1202,12 +935,89 @@ message MyMessage { } ``` -## Preserving proto2 or proto3 Behavior {#preserving} +### `features.(pb.go).strip_enum_prefix` {#go-strip_enum_prefix} + +**Languages:** Go + +Enum values are not scoped by their containing enum name, so +[prefixing every value with the enum name is recommended](/programming-guides/style#enums): + +```proto +edition = "2024"; + +enum Strip { + STRIP_ZERO = 0; + STRIP_ONE = 1; +} +``` + +However, the generated Go code will now contain two prefixes! + +```go +type Strip int32 + +const ( + Strip_STRIP_ZERO Strip = 0 + Strip_STRIP_ONE Strip = 1 +) +``` + +The language-specific `strip_enum_prefix` feature determines whether the Go code +generator strips the repetitive prefix or not. + +**Values available:** + +* `STRIP_ENUM_PREFIX_KEEP`: Keep the name as-is, even if repetitive. +* `STRIP_ENUM_PREFIX_GENERATE_BOTH`: Generate both, a full name and a stripped + name (to help with migrating your Go code). +* `STRIP_ENUM_PREFIX_STRIP`: Strip the enum name prefix from enum value names. + +**Applicable to the following scopes:** Enum, File + +**Added in:** 2024 + +**Default behavior per syntax/edition:** + +Syntax/edition | Default +-------------- | ------------------------ +2024 | `STRIP_ENUM_PREFIX_KEEP` + +**Note:** Feature settings on different schema elements +[have different scopes](#cascading). + +You can set the `strip_enum_prefix` feature in edition 2024 (or newer) .proto +files: + +```proto +edition = "2024"; + +import "third_party/golang/protobuf/v2/src/google/protobuf/go_features.proto"; + +option features.(pb.go).strip_enum_prefix = STRIP_ENUM_PREFIX_STRIP; + +enum Strip { + STRIP_ZERO = 0; + STRIP_ONE = 1; +} +``` + +The generated Go code will now strip the `STRIP` prefix: + +```go +type Strip int32 + +const ( + Strip_ZERO Strip = 0 + Strip_ONE Strip = 1 +) +``` + +## Preserving proto2 or proto3 Behavior in Edition 2023 {#preserving} You may want to move to the editions format but not deal with updates to the way that generated code behaves yet. This section shows the changes that the -Prototiller tool makes to your .proto files to make editions-based protos behave -like a proto2 or proto3 file. +Prototiller tool makes to your .proto files to make edition-2023-based protos +behave like a proto2 or proto3 file. After these changes are made at the file level, you get the proto2 or proto3 defaults. You can override at lower levels (message level, field level) to diff --git a/content/editions/overview.md b/content/editions/overview.md index f5b1b3a2..ea047cf4 100644 --- a/content/editions/overview.md +++ b/content/editions/overview.md @@ -28,6 +28,10 @@ The examples in this topic show edition 2024 features, but edition 2024 is currently in **pre-release review** and is not yet recommended for production code. +The examples in this topic show edition 2024 features, but edition 2024 is +currently in **pre-release review** and is not yet recommended for production +code. + ## Lifecycle of a Feature {#lifecycles} Editions provide the fundamental increments for the lifecycle of a feature. diff --git a/content/includes/version-tables.css b/content/includes/version-tables.css index 6e18e3dc..678ccf5c 100644 --- a/content/includes/version-tables.css +++ b/content/includes/version-tables.css @@ -95,36 +95,36 @@ table.version-chart td.active { /* latest release column */ /* * How to advance the selection of the latest release: - * Replace class 'y25q2' in the following selectors with 'y25q3' (the class + * Replace class 'y25q3' in the following selectors with 'y25q4' (the class * referring to the quarter of the next release). Please also update this * instruction as a courtesy to the next maintainer. */ /* visually replace 'yyQq' heading with string 'Latest' */ -table.version-chart th.y25q2 span { +table.version-chart th.y25q3 span { display: none; } -table.version-chart th.y25q2::after { +table.version-chart th.y25q3::after { content: "Latest" } /* draw a focus rectangle around the latest release column */ -table.version-chart th.y25q2 { +table.version-chart th.y25q3 { border-top: 2px solid #e06666 !important; border-left: 2px solid #e06666 !important; border-right: 2px solid #e06666 !important; } -table.version-chart td.y25q2 { +table.version-chart td.y25q3 { font-weight: bold; border-left: 2px solid #e06666 !important; border-right: 2px solid #e06666 !important; } -table.version-chart tr:last-child td.y25q2 { +table.version-chart tr:last-child td.y25q3 { border-bottom: 2px solid #e06666 !important; } /* future release columns */ -table.version-chart td:not(:has(~ .y25q2)):not(.y25q2) { +table.version-chart td:not(:has(~ .y25q3)):not(.y25q3) { font-style: italic; } diff --git a/content/programming-guides/enum.md b/content/programming-guides/enum.md index 4d1b1b6c..7ddba71b 100644 --- a/content/programming-guides/enum.md +++ b/content/programming-guides/enum.md @@ -110,6 +110,17 @@ open, and when importing from another editions file it uses the feature setting. All known C++ releases are out of conformance. When a `proto2` file imports an enum defined in a `proto3` file, C++ treats that field as a **closed** enum. +Under editions, this behavior is represented by the deprecated field feature +[`features.(pb.cpp).legacy_closed_enum`](/editions/features#legacy_closed_enum). +There are two options for moving to conformant behavior: + +* Remove the field feature. This is the recommended approach, but may cause + runtime behavior changes. Without the feature, unrecognized integers will + end up stored in the field cast to the enum type instead of being put into + the unknown field set. +* Change the enum to closed. This is discouraged, and can cause runtime + behavior if *anybody else* is using the enum. Unrecognized integers will end + up in the unknown field set instead of those fields. Under editions, this behavior is represented by the deprecated field feature [`features.(pb.cpp).legacy_closed_enum`](/editions/features#legacy_closed_enum). diff --git a/content/programming-guides/proto-limits.md b/content/programming-guides/proto-limits.md index 6b56da55..98708a4a 100644 --- a/content/programming-guides/proto-limits.md +++ b/content/programming-guides/proto-limits.md @@ -15,6 +15,8 @@ others. ## Number of Fields {#fields} +All messages are limited to 65,535 fields. + Message with only singular proto fields (such as Boolean): * ~2100 fields (proto2) diff --git a/content/programming-guides/proto3.md b/content/programming-guides/proto3.md index ca19824b..446a0b7e 100644 --- a/content/programming-guides/proto3.md +++ b/content/programming-guides/proto3.md @@ -879,6 +879,34 @@ file: import "myproject/other_protos.proto"; ``` +The protobuf compiler searches for imported files in a set of directories +specified using the `-I`/`--proto_path` flag. The path given in an `import` +statement is resolved relative to these directories. For more information on +using the compiler, see [Generating Your Classes](#generating). + +For example, consider the following directory structure: + +``` +my_project/ +├── protos/ +│ ├── main.proto +│ └── common/ +│ └── timestamp.proto +``` + +To use definitions from `timestamp.proto` within `main.proto`, you would run the +compiler from the `my_project` directory and set `--proto_path=protos`. The +`import` statement in `main.proto` would then be: + +```proto +// Located in my_project/protos/main.proto +import "common/timestamp.proto"; +``` + +In general you should set the `--proto_path` flag to the highest-level directory +that contains protos. This is often the root of the project, but in this example +it's in a separate `/protos` directory. + By default, you can use definitions only from directly imported `.proto` files. However, sometimes you may need to move a `.proto` file to a new location. Instead of moving the `.proto` file directly and updating all the call sites in @@ -915,12 +943,6 @@ import "old.proto"; // You use definitions from old.proto and new.proto, but not other.proto ``` -The protocol compiler searches for imported files in a set of directories -specified on the protocol compiler command line using the `-I`/`--proto_path` -flag. If no flag was given, it looks in the directory in which the compiler was -invoked. In general you should set the `--proto_path` flag to the root of your -project and use fully qualified names for all imports. - ### Using proto2 Message Types {#proto2} It's possible to import @@ -1739,7 +1761,7 @@ generator plugin for the compiler; you can find this and installation instructions in the [golang/protobuf](https://github.com/golang/protobuf/) repository on GitHub. -The Protocol Compiler is invoked as follows: +The protobuf compiler is invoked as follows: ```sh protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto diff --git a/content/programming-guides/style.md b/content/programming-guides/style.md index f9dc7501..0215bbed 100644 --- a/content/programming-guides/style.md +++ b/content/programming-guides/style.md @@ -9,6 +9,9 @@ This document provides a style guide for `.proto` files. By following these conventions, you'll make your protocol buffer message definitions and their corresponding classes consistent and easy to read. +Enforcement of the following style guidelines is controlled via +[enforce_naming_style](/editions/features#enforce-naming). + ## Standard File Formatting {#standard-file-formatting} * Keep the line length to 80 characters. diff --git a/content/reference/python/python-comparison.md b/content/reference/python/python-comparison.md new file mode 100644 index 00000000..48c66f77 --- /dev/null +++ b/content/reference/python/python-comparison.md @@ -0,0 +1,89 @@ ++++ +title = "Python Comparison" +weight = 751 +linkTitle = "Python Comparison" +description = "Describes how Python compares objects." +type = "docs" ++++ + +Because of how proto data is serialized, you cannot rely on the wire +representation of a proto message instance to determine if its content is the +same as another instance. A subset of the ways that a wire-format proto message +instance can vary include the following: + +* The protobuf schema changes in certain ways. +* A map field stores its values in a different order. +* The binary is built with different flags (such as opt vs. debug). +* The protobuf library is updated. + +Because of these ways that serialized data can vary, determining equality +involves other methods. + +## Comparison Methods {#methods} + +You can compare protocol buffer messages for equality using the standard Python +`==` operator. Comparing two objects using the `==` operator compares with +`message.ListFields()`. When testing, you can use `self.assertEqual(msg1, +msg2)`. + +Two messages are considered equal if they have the same type and all of their +corresponding fields are equal. The inequality operator `!=` is the exact +inverse of `==`. + +Message equality is recursive: for two messages to be equal, any nested messages +must also be equal. + +## Field Equality and Presence + +The equality check for fields is value-based. For fields with +[explicit presence](#singular-explicit), the presence of a field is also taken +into account. + +A field that is explicitly set to its default value is **not** considered equal +to a field that is unset. + +For example, consider the following message which has an explicit presence +field: + +```proto +edition = "2023"; +message MyMessage { + int32 value = 1; // 'explicit' presence by default in Editions +} +``` + +If you create two instances, one where `value` is unset and one where `value` is +explicitly set to `0`, they will not be equal: + +```python +msg1 = MyMessage() +msg2 = MyMessage() +msg2.value = 0 + +assert not msg1.HasField("value") +assert msg2.HasField("value") +assert msg1 != msg2 +``` + +This same principle applies to sub-message fields: an unset sub-message is not +equal to a sub-message that is present but empty (a default instance of the +sub-message class). + +For fields with [implicit presence](#singular-implicit), since presence cannot +be tracked, the field is always compared by its value against the corresponding +field in the other message. + +This behavior, where presence is part of the equality check, is different from +how some other languages or protobuf libraries might handle equality, where +unset fields and fields set to their default value are sometimes treated as +equivalent (often for wire-format compatibility). In Python, `==` performs a +stricter check. + +## Other Field Types {#other-types} + +* **Repeated fields** are equal if they have the same number of elements and + each corresponding element is equal. The order of elements matters. +* **Map fields** are equal if they have the same set of key-value pairs. The + order of pairs does not matter. +* **Floating-point fields** (`float` and `double`) are compared by value. Be + aware of the usual caveats with floating-point comparisons.