pw_rpc: Documentation update

- Add documentation for the pw_rpc Python package.
- Discuss using proto3 instead of proto2.
- Add "other_deps" to pw_doc_group for expressing arbitrary deps.

Change-Id: I3c8baf2a7986a7d06721d76278794201259815a7
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/33104
Reviewed-by: Keir Mierle <keir@google.com>
Commit-Queue: Wyatt Hepler <hepler@google.com>
diff --git a/pw_docgen/docs.gni b/pw_docgen/docs.gni
index a6354bb..50bf8d6 100644
--- a/pw_docgen/docs.gni
+++ b/pw_docgen/docs.gni
@@ -29,6 +29,7 @@
 #   inputs: Additional resource files for the docs, such as images.
 #   group_deps: Other pw_doc_group targets on which this group depends.
 #   report_deps: Report card targets on which documentation depends.
+#   other_deps: Dependencies on any other types of targets.
 template("pw_doc_group") {
   assert(defined(invoker.sources), "pw_doc_group requires a list of sources")
 
@@ -45,6 +46,9 @@
   if (defined(invoker.report_deps)) {
     _all_deps += invoker.report_deps
   }
+  if (defined(invoker.other_deps)) {
+    _all_deps += invoker.other_deps
+  }
 
   # Create a group containing the source and input files so that docs are
   # rebuilt on file modifications.
diff --git a/pw_protobuf/py/pw_protobuf/plugin.py b/pw_protobuf/py/pw_protobuf/plugin.py
index 3702a6e..058a29d 100755
--- a/pw_protobuf/py/pw_protobuf/plugin.py
+++ b/pw_protobuf/py/pw_protobuf/plugin.py
@@ -54,6 +54,11 @@
     request = plugin_pb2.CodeGeneratorRequest.FromString(data)
     response = plugin_pb2.CodeGeneratorResponse()
     process_proto_request(request, response)
+
+    # Declare that this plugin supports optional fields in proto3.
+    response.supported_features |= (  # type: ignore[attr-defined]
+        response.FEATURE_PROTO3_OPTIONAL)  # type: ignore[attr-defined]
+
     sys.stdout.buffer.write(response.SerializeToString())
     return 0
 
diff --git a/pw_rpc/BUILD.gn b/pw_rpc/BUILD.gn
index bb22728..06ba545 100644
--- a/pw_rpc/BUILD.gn
+++ b/pw_rpc/BUILD.gn
@@ -146,7 +146,10 @@
     "pw_rpc_protos/echo.proto",
     "pw_rpc_protos/internal/packet.proto",
   ]
-  group_deps = [ "nanopb:docs" ]
+  group_deps = [
+    "nanopb:docs",
+    "py:docs",
+  ]
   report_deps = [ ":server_size" ]
 }
 
diff --git a/pw_rpc/docs.rst b/pw_rpc/docs.rst
index 7235232..4f76398 100644
--- a/pw_rpc/docs.rst
+++ b/pw_rpc/docs.rst
@@ -6,6 +6,15 @@
 The ``pw_rpc`` module provides a system for defining and invoking remote
 procedure calls (RPCs) on a device.
 
+This document discusses the ``pw_rpc`` protocol and its C++ implementation.
+``pw_rpc`` implementations for other languages are described in their own
+documents:
+
+.. toctree::
+  :maxdepth: 1
+
+  py/docs
+
 .. admonition:: Try it out!
 
   For a quick intro to ``pw_rpc``, see the
@@ -55,6 +64,39 @@
     sources = [ "foo_bar/the_service.proto" ]
   }
 
+.. admonition:: proto2 or proto3 syntax?
+
+  Always use proto3 syntax rather than proto2 for new protocol buffers. Proto2
+  protobufs can be compiled for ``pw_rpc``, but they are not as well supported
+  as proto3. Specifically, ``pw_rpc`` lacks support for non-zero default values
+  in proto2. When using Nanopb with ``pw_rpc``, proto2 response protobufs with
+  non-zero field defaults should be manually initialized to the default struct.
+
+  In the past, proto3 was sometimes avoided because it lacked support for field
+  presence detection. Fortunately, this has been fixed: proto3 now supports
+  ``optional`` fields, which are equivalent to proto2 ``optional`` fields.
+
+  If you need to distinguish between a default-valued field and a missing field,
+  mark the field as ``optional``. The presence of the field can be detected
+  with a ``HasField(name)`` or ``has_<field>`` member, depending on the library.
+
+  Optional fields have some overhead --- default-valued fields are included in
+  the encoded proto, and, if using Nanopb, the proto structs have a
+  ``has_<field>`` flag for each optional field. Use plain fields if field
+  presence detection is not needed.
+
+  .. code-block:: protobuf
+
+    syntax = "proto3";
+
+    message MyMessage {
+      // Leaving this field unset is equivalent to setting it to 0.
+      int32 number = 1;
+
+      // Setting this field to 0 is different from leaving it unset.
+      optional int32 other_number = 2;
+    }
+
 2. RPC code generation
 ----------------------
 ``pw_rpc`` generates a C++ header file for each ``.proto`` file. This header is
diff --git a/pw_rpc/py/BUILD.gn b/pw_rpc/py/BUILD.gn
index 5727946..2c627e5 100644
--- a/pw_rpc/py/BUILD.gn
+++ b/pw_rpc/py/BUILD.gn
@@ -15,6 +15,7 @@
 import("//build_overrides/pigweed.gni")
 
 import("$dir_pw_build/python.gni")
+import("$dir_pw_docgen/docs.gni")
 
 pw_python_package("py") {
   setup = [ "setup.py" ]
@@ -51,3 +52,8 @@
   python_test_deps = [ "$dir_pw_build/py" ]
   pylintrc = "$dir_pigweed/.pylintrc"
 }
+
+pw_doc_group("docs") {
+  sources = [ "docs.rst" ]
+  other_deps = [ ":py" ]
+}
diff --git a/pw_rpc/py/docs.rst b/pw_rpc/py/docs.rst
new file mode 100644
index 0000000..002e260
--- /dev/null
+++ b/pw_rpc/py/docs.rst
@@ -0,0 +1,18 @@
+.. _module-pw_rpc-py:
+
+---------------------
+pw_rpc Python package
+---------------------
+The ``pw_rpc`` Python package makes it possible to call Pigweed RPCs from
+Python. The package includes a ``pw_rpc`` client library, as well as tools for
+creating a ``pw_rpc`` console.
+
+pw_rpc.client
+=============
+.. automodule:: pw_rpc.client
+  :members: Client, ClientImpl
+
+pw_rpc.console_tools
+====================
+.. automodule:: pw_rpc.console_tools
+  :members:
diff --git a/pw_rpc/py/pw_rpc/client.py b/pw_rpc/py/pw_rpc/client.py
index d82a4de..32689e6 100644
--- a/pw_rpc/py/pw_rpc/client.py
+++ b/pw_rpc/py/pw_rpc/client.py
@@ -11,7 +11,7 @@
 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 # License for the specific language governing permissions and limitations under
 # the License.
-"""Creates an RPC client."""
+"""Provides a pw_rpc client for Python."""
 
 import abc
 from dataclasses import dataclass