158 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			158 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
| Migrating Guide
 | |
| ===============
 | |
| 
 | |
| Google's protocolbuffers
 | |
| ------------------------
 | |
| 
 | |
| betterproto has a mostly 1 to 1 drop in replacement for Google's protocolbuffers (after
 | |
| regenerating your protobufs of course) although there are some minor differences.
 | |
| 
 | |
| .. note::
 | |
| 
 | |
|     betterproto implements the same basic methods including:
 | |
| 
 | |
|         - :meth:`betterproto.Message.FromString`
 | |
|         - :meth:`betterproto.Message.SerializeToString`
 | |
| 
 | |
|     for compatibility purposes, however it is important to note that these are
 | |
|     effectively aliases for :meth:`betterproto.Message.parse` and
 | |
|     :meth:`betterproto.Message.__bytes__` respectively.
 | |
| 
 | |
| 
 | |
| Determining if a message was sent
 | |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| Sometimes it is useful to be able to determine whether a message has been sent on
 | |
| the wire. This is how the Google wrapper types work to let you know whether a value is
 | |
| unset (set as the default/zero value), or set as something else, for example.
 | |
| 
 | |
| Use ``betterproto.serialized_on_wire(message)`` to determine if it was sent. This is
 | |
| a little bit different from the official Google generated Python code, and it lives
 | |
| outside the generated ``Message`` class to prevent name clashes. Note that it only
 | |
| supports Proto 3 and thus can only be used to check if ``Message`` fields are set.
 | |
| You cannot check if a scalar was sent on the wire.
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|     # Old way (official Google Protobuf package)
 | |
|     >>> mymessage.HasField('myfield')
 | |
|     True
 | |
| 
 | |
|     # New way (this project)
 | |
|     >>> betterproto.serialized_on_wire(mymessage.myfield)
 | |
|     True
 | |
| 
 | |
| 
 | |
| One-of Support
 | |
| ~~~~~~~~~~~~~~
 | |
| 
 | |
| Protobuf supports grouping fields in a oneof clause. Only one of the fields in the group
 | |
| may be set at a given time. For example, given the proto:
 | |
| 
 | |
| .. code-block:: proto
 | |
| 
 | |
|     syntax = "proto3";
 | |
| 
 | |
|     message Test {
 | |
|       oneof foo {
 | |
|         bool on = 1;
 | |
|         int32 count = 2;
 | |
|         string name = 3;
 | |
|       }
 | |
|     }
 | |
| 
 | |
| You can use ``betterproto.which_one_of(message, group_name)`` to determine which of the
 | |
| fields was set. It returns a tuple of the field name and value, or a blank string and
 | |
| ``None`` if unset. Again this is a little different than the official Google code
 | |
| generator:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|     # Old way (official Google protobuf package)
 | |
|     >>> message.WhichOneof("group")
 | |
|     "foo"
 | |
| 
 | |
|     # New way (this project)
 | |
|     >>> betterproto.which_one_of(message, "group")
 | |
|     ("foo", "foo's value")
 | |
| 
 | |
| 
 | |
| Well-Known Google Types
 | |
| ~~~~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| Google provides several well-known message types like a timestamp, duration, and several
 | |
| wrappers used to provide optional zero value support. Each of these has a special JSON
 | |
| representation and is handled a little differently from normal messages. The Python
 | |
| mapping for these is as follows:
 | |
| 
 | |
| +-------------------------------+-----------------------------------------------+--------------------------+
 | |
| | ``Google Message``            | ``Python Type``                               | ``Default``              |
 | |
| +===============================+===============================================+==========================+
 | |
| | ``google.protobuf.duration``  | :class:`datetime.timedelta`                   | ``0``                    |
 | |
| +-------------------------------+-----------------------------------------------+--------------------------+
 | |
| | ``google.protobuf.timestamp`` | ``Timezone-aware`` :class:`datetime.datetime` | ``1970-01-01T00:00:00Z`` |
 | |
| +-------------------------------+-----------------------------------------------+--------------------------+
 | |
| | ``google.protobuf.*Value``    | ``Optional[...]``/``None``                    | ``None``                 |
 | |
| +-------------------------------+-----------------------------------------------+--------------------------+
 | |
| | ``google.protobuf.*``         | ``betterproto.lib.google.protobuf.*``         | ``None``                 |
 | |
| +-------------------------------+-----------------------------------------------+--------------------------+
 | |
| 
 | |
| 
 | |
| For the wrapper types, the Python type corresponds to the wrapped type, e.g.
 | |
| ``google.protobuf.BoolValue`` becomes ``Optional[bool]`` while
 | |
| ``google.protobuf.Int32Value`` becomes ``Optional[int]``. All of the optional values
 | |
| default to None, so don't forget to check for that possible state.
 | |
| 
 | |
| Given:
 | |
| 
 | |
| .. code-block:: proto
 | |
| 
 | |
|     syntax = "proto3";
 | |
| 
 | |
|     import "google/protobuf/duration.proto";
 | |
|     import "google/protobuf/timestamp.proto";
 | |
|     import "google/protobuf/wrappers.proto";
 | |
| 
 | |
|     message Test {
 | |
|       google.protobuf.BoolValue maybe = 1;
 | |
|       google.protobuf.Timestamp ts = 2;
 | |
|       google.protobuf.Duration duration = 3;
 | |
|     }
 | |
| 
 | |
| You can use it as such:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|     >>> t = Test().from_dict({"maybe": True, "ts": "2019-01-01T12:00:00Z", "duration": "1.200s"})
 | |
|     >>> t
 | |
|     Test(maybe=True, ts=datetime.datetime(2019, 1, 1, 12, 0, tzinfo=datetime.timezone.utc), duration=datetime.timedelta(seconds=1, microseconds=200000))
 | |
| 
 | |
|     >>> t.ts - t.duration
 | |
|     datetime.datetime(2019, 1, 1, 11, 59, 58, 800000, tzinfo=datetime.timezone.utc)
 | |
| 
 | |
|     >>> t.ts.isoformat()
 | |
|     '2019-01-01T12:00:00+00:00'
 | |
| 
 | |
|     >>> t.maybe = None
 | |
|     >>> t.to_dict()
 | |
|     {'ts': '2019-01-01T12:00:00Z', 'duration': '1.200s'}
 | |
| 
 | |
| 
 | |
| [1.2.5] to [2.0.0b1]
 | |
| --------------------
 | |
| 
 | |
| Updated package structures
 | |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| Generated code now strictly follows the *package structure* of the ``.proto`` files.
 | |
| Consequently ``.proto`` files without a package will be combined in a single
 | |
| ``__init__.py`` file. To avoid overwriting existing ``__init__.py`` files, its best
 | |
| to compile into a dedicated subdirectory.
 | |
| 
 | |
| Upgrading:
 | |
| 
 | |
| - Remove your previously compiled ``.py`` files.
 | |
| - Create a new *empty* directory, e.g. ``generated`` or ``lib/generated/proto`` etc.
 | |
| - Regenerate your python files into this directory
 | |
| - Update import statements, e.g. ``import ExampleMessage from generated``
 |