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