Support proto3 field presence (#281)
* Update protobuf pregenerated files * Update grpcio-tools to latest version * Implement proto3 field presence * Fix to_dict with None optional fields. * Add test with optional enum * Properly support optional enums * Add tests for 64-bit ints and floats * Support field presence for int64 types * Fix oneof serialization with proto3 field presence (#292) = Description The serialization of a oneof message that contains a message with fields with explicit presence was buggy. For example: ``` message A { oneof kind { B b = 1; C c = 2; } } message B {} message C { optional bool z = 1; } ``` Serializing `A(b=B())` would lead to this payload: ``` 0A # tag1, length delimited 00 # length: 0 12 # tag2, length delimited 00 # length: 0 ``` Which when deserialized, leads to the message `A(c=C())`. = Explanation The issue lies in the post_init method. All fields are introspected, and if different from PLACEHOLDER, the message is marked as having been "serialized_on_wire". Then, when serializing `A(b=B())`, we go through each field of the oneof: - field 'b': this is the selected field from the group, so it is serialized - field 'c': marked as 'serialized_on_wire', so it is added as well. = Fix The issue is that support for explicit presence changed the default value from PLACEHOLDER to None. This breaks the post_init method in that case, which is relatively easy to fix: if a field is optional, and set to None, this is considered as the default value (which it is). This fix however has a side-effect: the group_current for this field (the oneof trick for explicit presence) is no longer set. This changes the behavior when serializing the message in JSON: as the value is the default one (None), and the group is not set (which would force the serialization of the field), so None fields are no longer serialized in JSON. This break one test, and will be fixed in the next commit. * fix: do not serialize None fields in JSON format This is linked to the fix from the previous commit: after it, scalar None fields were not included in the JSON format, but some were still included. This is all cleaned up: None fields are not added in JSON by default, as they indicate the default value of fields with explicit presence. However, if `include_default_values is set, they are included. * Fix: use builtin annotation prefix * Remove comment Co-authored-by: roblabla <unfiltered@roblab.la> Co-authored-by: Vincent Thiberville <vthib@pm.me>
This commit is contained in:
parent
671c0ff4ac
commit
d77f44ebb7
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -66,4 +66,4 @@ jobs:
|
||||
|
||||
- name: Execute test suite
|
||||
shell: bash
|
||||
run: poetry run pytest tests/
|
||||
run: poetry run python -m pytest tests/
|
||||
|
234
poetry.lock
generated
234
poetry.lock
generated
@ -229,7 +229,7 @@ docs = ["sphinx"]
|
||||
|
||||
[[package]]
|
||||
name = "grpcio"
|
||||
version = "1.36.1"
|
||||
version = "1.40.0"
|
||||
description = "HTTP/2-based RPC framework"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -239,18 +239,18 @@ python-versions = "*"
|
||||
six = ">=1.5.2"
|
||||
|
||||
[package.extras]
|
||||
protobuf = ["grpcio-tools (>=1.36.1)"]
|
||||
protobuf = ["grpcio-tools (>=1.40.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "grpcio-tools"
|
||||
version = "1.36.1"
|
||||
version = "1.40.0"
|
||||
description = "Protobuf code generator for gRPC"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
grpcio = ">=1.36.1"
|
||||
grpcio = ">=1.40.0"
|
||||
protobuf = ">=3.5.0.post1,<4.0dev"
|
||||
|
||||
[[package]]
|
||||
@ -470,15 +470,12 @@ tomlkit = ">=0.6.0,<1.0.0"
|
||||
|
||||
[[package]]
|
||||
name = "protobuf"
|
||||
version = "3.15.7"
|
||||
version = "3.18.0"
|
||||
description = "Protocol Buffers"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
six = ">=1.9"
|
||||
|
||||
[[package]]
|
||||
name = "py"
|
||||
version = "1.10.0"
|
||||
@ -1073,100 +1070,96 @@ greenlet = [
|
||||
{file = "greenlet-1.0.0.tar.gz", hash = "sha256:719e169c79255816cdcf6dccd9ed2d089a72a9f6c42273aae12d55e8d35bdcf8"},
|
||||
]
|
||||
grpcio = [
|
||||
{file = "grpcio-1.36.1-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:e3a83c5db16f95daac1d96cf3c9018d765579b5a29bb336758d793028e729921"},
|
||||
{file = "grpcio-1.36.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:c18739fecb90760b183bfcb4da1cf2c6bf57e38f7baa2c131d5f67d9a4c8365d"},
|
||||
{file = "grpcio-1.36.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:f6efa62ca1fe02cd34ec35f53446f04a15fe2c886a4e825f5679936a573d2cbf"},
|
||||
{file = "grpcio-1.36.1-cp27-cp27m-win32.whl", hash = "sha256:9a18299827a70be0507f98a65393b1c7f6c004fe2ca995fe23ffac534dd187a7"},
|
||||
{file = "grpcio-1.36.1-cp27-cp27m-win_amd64.whl", hash = "sha256:8a89190de1985a54ef311650cf9687ffb81de038973fd32e452636ddae36b29f"},
|
||||
{file = "grpcio-1.36.1-cp27-cp27mu-linux_armv7l.whl", hash = "sha256:3e75643d21db7d68acd541d3fec66faaa8061d12b511e101b529ff12a276bb9b"},
|
||||
{file = "grpcio-1.36.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:3c5204e05e18268dd6a1099ca6c106fd9d00bcae1e37d5a5186094c55044c941"},
|
||||
{file = "grpcio-1.36.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:24d4c2c5e540e666c52225953d6813afc8ccf9bf46db6a72edd4e8d606656248"},
|
||||
{file = "grpcio-1.36.1-cp35-cp35m-linux_armv7l.whl", hash = "sha256:4dc7295dc9673f7af22c1e38c2a2c24ecbd6773a4c5ed5a46ed38ad4dcf2bf6c"},
|
||||
{file = "grpcio-1.36.1-cp35-cp35m-macosx_10_10_intel.whl", hash = "sha256:f241116d4bf1a8037ff87f16914b606390824e50902bdbfa2262e855fbf07fe5"},
|
||||
{file = "grpcio-1.36.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:1056b558acfd575d774644826df449e1402a03e456a3192fafb6b06d1069bf80"},
|
||||
{file = "grpcio-1.36.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:52ec563da45d06319224ebbda53501d25594de64ee1b2786e119ba4a2f1ce40c"},
|
||||
{file = "grpcio-1.36.1-cp35-cp35m-manylinux2014_i686.whl", hash = "sha256:7cbeac9bbe6a4a7fce4a89c892c249135dd9f5f5219ede157174c34a456188f0"},
|
||||
{file = "grpcio-1.36.1-cp35-cp35m-manylinux2014_x86_64.whl", hash = "sha256:2abaa9f0d83bd0b26f6d0d1fc4b97d73bde3ceac36ab857f70d3cabcf31c5c79"},
|
||||
{file = "grpcio-1.36.1-cp35-cp35m-win32.whl", hash = "sha256:02030e1afd3247f2b159df9dff959ec79dd4047b1c4dd4eec9e3d1642efbd504"},
|
||||
{file = "grpcio-1.36.1-cp35-cp35m-win_amd64.whl", hash = "sha256:eafafc7e040e36aa926edc731ab52c23465981888779ae64bfc8ad85888ed4f3"},
|
||||
{file = "grpcio-1.36.1-cp36-cp36m-linux_armv7l.whl", hash = "sha256:1030e74ddd0fa6e3bad7944f0c68cf1251b15bcd70641f0ad3858fdf2b8602a0"},
|
||||
{file = "grpcio-1.36.1-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:b003e24339030ed356f59505d1065b89e1f443ef41ce71ca9069be944c0d2e6b"},
|
||||
{file = "grpcio-1.36.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:76daa3c4d58fcf40f7969bdb4270335e96ee0382a050cadcd97d7332cd0251a3"},
|
||||
{file = "grpcio-1.36.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:f591597bb25eae0094ead5a965555e911453e5f35fdbdaa83be11ef107865697"},
|
||||
{file = "grpcio-1.36.1-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:cbd82c479338fc1c0e5c3db09752b61fe47d40c6e38e4be8657153712fa76674"},
|
||||
{file = "grpcio-1.36.1-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:7e32bc01dfaa7a51c547379644ea619a2161d6969affdac3bbd173478d26673d"},
|
||||
{file = "grpcio-1.36.1-cp36-cp36m-win32.whl", hash = "sha256:5378189fb897567f4929f75ab67a3e0da4f8967806246cb9cfa1fa06bfbdb0d5"},
|
||||
{file = "grpcio-1.36.1-cp36-cp36m-win_amd64.whl", hash = "sha256:3a6295aa692806218e97bb687a71cd768450ed99e2acddc488f18d738edef463"},
|
||||
{file = "grpcio-1.36.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:6f6f8a8b57e40347d0bf32c2135037dae31d63d3b19007b4c426a11b76deaf65"},
|
||||
{file = "grpcio-1.36.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:4c05ed54b2a00df01e633bebec819b512bf0c60f8f5b3b36dd344dc673b02fea"},
|
||||
{file = "grpcio-1.36.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e1b9e906aa6f7577016e86ed7f3a69cae7dab4e41356584dc7980f76ea65035f"},
|
||||
{file = "grpcio-1.36.1-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:a602d6b30760bbbb2fe776caaa914a0d404636cafc3f2322718bf8002d7b1e55"},
|
||||
{file = "grpcio-1.36.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:dee9971aef20fc09ed897420446c4d0926cd1d7630f343333288523ca5b44bb2"},
|
||||
{file = "grpcio-1.36.1-cp37-cp37m-win32.whl", hash = "sha256:ed16bfeda02268e75e038c58599d52afc7097d749916c079b26bc27a66900f7d"},
|
||||
{file = "grpcio-1.36.1-cp37-cp37m-win_amd64.whl", hash = "sha256:85a6035ae75ce964f78f19cf913938596ccf068b149fcd79f4371268bcb9aa7c"},
|
||||
{file = "grpcio-1.36.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:6b30682180053eebc87802c2f249d2f59b430e1a18e8808575dde0d22a968b2c"},
|
||||
{file = "grpcio-1.36.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:5e4920a8fb5d17b2c5ba980db0ac1c925bbee3e5d70e96da3ec4fb1c8600d68f"},
|
||||
{file = "grpcio-1.36.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f7740d9d9451f3663df11b241ac05cafc0efaa052d2fdca6640c4d3748eaf6e2"},
|
||||
{file = "grpcio-1.36.1-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:20b7c4c5513e1135a2261e56830c0e710f205fee92019b92fe132d7f16a5cfd8"},
|
||||
{file = "grpcio-1.36.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:216fbd2a488e74c3b96e240e4054c85c4c99102a439bc9f556936991643f43bc"},
|
||||
{file = "grpcio-1.36.1-cp38-cp38-win32.whl", hash = "sha256:7863c2a140e829b1f4c6d67bf0bf15e5321ac4766d0a295e2682970d9dd4b091"},
|
||||
{file = "grpcio-1.36.1-cp38-cp38-win_amd64.whl", hash = "sha256:f214076eb13da9e65c1aa9877b51fca03f51a82bd8691358e1a1edd9ff341330"},
|
||||
{file = "grpcio-1.36.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:ec753c022b39656f88409fbf9f2d3b28497e3f17aa678f884d78776b41ebe6bd"},
|
||||
{file = "grpcio-1.36.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:0648a6d5d7ddcd9c8462d7d961660ee024dad6b88152ee3a521819e611830edf"},
|
||||
{file = "grpcio-1.36.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:45ea10dd133a43b10c0b4326834107ebccfee25dab59b312b78e018c2d72a1f0"},
|
||||
{file = "grpcio-1.36.1-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:bab743cdac1d6d8326c65d1d091d0740b39966dfab06519f74a03b3d128b8454"},
|
||||
{file = "grpcio-1.36.1-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:09af8ceb91860086216edc6e5ea15f9beb2cf81687faa43b7c03216f5b73e244"},
|
||||
{file = "grpcio-1.36.1-cp39-cp39-win32.whl", hash = "sha256:f3f70505207ee1cee65f60a799fd8e06e07861409aa0d55d834825a79b40c297"},
|
||||
{file = "grpcio-1.36.1-cp39-cp39-win_amd64.whl", hash = "sha256:f22c11772eff25ba1ca536e760b8c34ba56f2a9d66b6842cb11770a8f61f879d"},
|
||||
{file = "grpcio-1.36.1.tar.gz", hash = "sha256:a66ea59b20f3669df0f0c6a3bd57b985e5b2d1dcf3e4c29819bb8dc232d0fd38"},
|
||||
{file = "grpcio-1.40.0-cp35-cp35m-macosx_10_10_intel.whl", hash = "sha256:6f8f581787e739945e6cda101f312ea8a7e7082bdbb4993901eb828da6a49092"},
|
||||
{file = "grpcio-1.40.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:a4389e26a8f9338ca91effdc5436dfec67d6ecd296368dba115799ae8f8e5bdb"},
|
||||
{file = "grpcio-1.40.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:fb06708e3d173e387326abcd5182d52beb60e049db5c3d317bd85509e938afdc"},
|
||||
{file = "grpcio-1.40.0-cp35-cp35m-manylinux2014_i686.whl", hash = "sha256:f06e07161c21391682bfcac93a181a037a8aa3d561546690e9d0501189729aac"},
|
||||
{file = "grpcio-1.40.0-cp35-cp35m-manylinux2014_x86_64.whl", hash = "sha256:5ff0dcf66315f3f00e1a8eb7244c6a49bdb0cc59bef4fb65b9db8adbd78e6acb"},
|
||||
{file = "grpcio-1.40.0-cp35-cp35m-win32.whl", hash = "sha256:ba9dd97ea1738be3e81d34e6bab8ff91a0b80668a4ec81454b283d3c828cebde"},
|
||||
{file = "grpcio-1.40.0-cp35-cp35m-win_amd64.whl", hash = "sha256:e12d776a240fee3ebd002519c02d165d94ec636d3fe3d6185b361bfc9a2d3106"},
|
||||
{file = "grpcio-1.40.0-cp36-cp36m-linux_armv7l.whl", hash = "sha256:6b9b432f5665dfc802187384693b6338f05c7fc3707ebf003a89bd5132074e27"},
|
||||
{file = "grpcio-1.40.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:886d056f5101ac513f4aefe4d21a816d98ee3f9a8e77fc3bcb4ae1a3a24efe26"},
|
||||
{file = "grpcio-1.40.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:b1b34e5a6f1285d1576099c663dae28c07b474015ed21e35a243aff66a0c2aed"},
|
||||
{file = "grpcio-1.40.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:17ed13d43450ef9d1f9b78cc932bcf42844ca302235b93026dfd07fb5208d146"},
|
||||
{file = "grpcio-1.40.0-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:e19de138199502d575fcec5cf68ae48815a6efe7e5c0d0b8c97eba8c77ae9f0e"},
|
||||
{file = "grpcio-1.40.0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:a812164ceb48cb62c3217bd6245274e693c624cc2ac0c1b11b4cea96dab054dd"},
|
||||
{file = "grpcio-1.40.0-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:eedc8c3514c10b6f11c6f406877e424ca29610883b97bb97e33b1dd2a9077f6c"},
|
||||
{file = "grpcio-1.40.0-cp36-cp36m-win32.whl", hash = "sha256:1708a0ba90c798b4313f541ffbcc25ed47e790adaafb02111204362723dabef0"},
|
||||
{file = "grpcio-1.40.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d760a66c9773780837915be85a39d2cd4ab42ef32657c5f1d28475e23ab709fc"},
|
||||
{file = "grpcio-1.40.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:8a35b5f87247c893b01abf2f4f7493a18c2c5bf8eb3923b8dd1654d8377aa1a7"},
|
||||
{file = "grpcio-1.40.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:45704b9b5b85f9bcb027f90f2563d11d995c1b870a9ee4b3766f6c7ff6fc3505"},
|
||||
{file = "grpcio-1.40.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:4967949071c9e435f9565ec2f49700cebeda54836a04710fe21f7be028c0125a"},
|
||||
{file = "grpcio-1.40.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:1f9ccc9f5c0d5084d1cd917a0b5ff0142a8d269d0755592d751f8ce9e7d3d7f1"},
|
||||
{file = "grpcio-1.40.0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:5729ca9540049f52c2e608ca110048cfabab3aeaa0d9f425361d9f8ba8506cac"},
|
||||
{file = "grpcio-1.40.0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:edddc849bed3c5dfe215a9f9532a9bd9f670b57d7b8af603be80148b4c69e9a8"},
|
||||
{file = "grpcio-1.40.0-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:49155dfdf725c0862c428039123066b25ce61bd38ce50a21ce325f1735aac1bd"},
|
||||
{file = "grpcio-1.40.0-cp37-cp37m-win32.whl", hash = "sha256:913916823efa2e487b2ee9735b7759801d97fd1974bacdb1900e3bbd17f7d508"},
|
||||
{file = "grpcio-1.40.0-cp37-cp37m-win_amd64.whl", hash = "sha256:24277aab99c346ca36a1aa8589a0624e19a8e6f2b74c83f538f7bb1cc5ee8dbc"},
|
||||
{file = "grpcio-1.40.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:a66a30513d2e080790244a7ac3d7a3f45001f936c5c2c9613e41e2a5d7a11794"},
|
||||
{file = "grpcio-1.40.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:e2367f2b18dd4ba64cdcd9f626a920f9ec2e8228630839dc8f4a424d461137ea"},
|
||||
{file = "grpcio-1.40.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:27dee6dcd1c04c4e9ceea49f6143003569292209d2c24ca100166660805e2440"},
|
||||
{file = "grpcio-1.40.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d271e52038dec0db7c39ad9303442d6087c55e09b900e2931b86e837cf0cbc2e"},
|
||||
{file = "grpcio-1.40.0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:41e250ec7cd7523bf49c815b5509d5821728c26fac33681d4b0d1f5f34f59f06"},
|
||||
{file = "grpcio-1.40.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:33dc4259fecb96e6eac20f760656b911bcb1616aa3e58b3a1d2f125714a2f5d3"},
|
||||
{file = "grpcio-1.40.0-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:72b7b8075ee822dad4b39c150d73674c1398503d389e38981e9e35a894c476de"},
|
||||
{file = "grpcio-1.40.0-cp38-cp38-win32.whl", hash = "sha256:a93490e6eff5fce3748fb2757cb4273dc21eb1b56732b8c9640fd82c1997b215"},
|
||||
{file = "grpcio-1.40.0-cp38-cp38-win_amd64.whl", hash = "sha256:d3b4b41eb0148fca3e6e6fc61d1332a7e8e7c4074fb0d1543f0b255d7f5f1588"},
|
||||
{file = "grpcio-1.40.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:fbe3b66bfa2c2f94535f6063f6db62b5b150d55a120f2f9e1175d3087429c4d9"},
|
||||
{file = "grpcio-1.40.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:ecfd80e8ea03c46b3ea7ed37d2040fcbfe739004b9e4329b8b602d06ac6fb113"},
|
||||
{file = "grpcio-1.40.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:d487b4daf84a14741ca1dc1c061ffb11df49d13702cd169b5837fafb5e84d9c0"},
|
||||
{file = "grpcio-1.40.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c26de909cfd54bacdb7e68532a1591a128486af47ee3a5f828df9aa2165ae457"},
|
||||
{file = "grpcio-1.40.0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:1d9eabe2eb2f78208f9ae67a591f73b024488449d4e0a5b27c7fca2d6901a2d4"},
|
||||
{file = "grpcio-1.40.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:4c2baa438f51152c9b7d0835ff711add0b4bc5056c0f5df581a6112153010696"},
|
||||
{file = "grpcio-1.40.0-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:bf114be0023b145f7101f392a344692c1efd6de38a610c54a65ed3cba035e669"},
|
||||
{file = "grpcio-1.40.0-cp39-cp39-win32.whl", hash = "sha256:5f6d6b638698fa6decf7f040819aade677b583eaa21b43366232cb254a2bbac8"},
|
||||
{file = "grpcio-1.40.0-cp39-cp39-win_amd64.whl", hash = "sha256:005fe14e67291498989da67d454d805be31d57a988af28ed3a2a0a7cabb05c53"},
|
||||
{file = "grpcio-1.40.0.tar.gz", hash = "sha256:3d172158fe886a2604db1b6e17c2de2ab465fe0fe36aba2ec810ca8441cefe3a"},
|
||||
]
|
||||
grpcio-tools = [
|
||||
{file = "grpcio-tools-1.36.1.tar.gz", hash = "sha256:80ef584f7b917f575e4b8f2ec59cd4a4d98c2046e801a735f3136b05742a36a6"},
|
||||
{file = "grpcio_tools-1.36.1-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:ebbfdbff079bfc303a4e1d3da59302147d5cf4f1db2c412a074366149d93e89e"},
|
||||
{file = "grpcio_tools-1.36.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:d95dfefe156be02bcce4eb044ac7ff166c8a6c288d71bc3ed960d8b26bce2786"},
|
||||
{file = "grpcio_tools-1.36.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:7969915ac252d0e67f9cfd4f8b9d6bb546efc7b26bce34978a940e37ee4078d5"},
|
||||
{file = "grpcio_tools-1.36.1-cp27-cp27m-win32.whl", hash = "sha256:582b77e7a4905063d8071ac3685cefa38941799d5f4ea7b4519281a28cfc6752"},
|
||||
{file = "grpcio_tools-1.36.1-cp27-cp27m-win_amd64.whl", hash = "sha256:66d2a6237941199df0493e46b8a3123005b4dfde9af1b9572e8c54eb605a7567"},
|
||||
{file = "grpcio_tools-1.36.1-cp27-cp27mu-linux_armv7l.whl", hash = "sha256:702c3eb61a3cfddcaea04d2358c0390c2e189fe42b64a92239df8292366ab4df"},
|
||||
{file = "grpcio_tools-1.36.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ce621375bc7dfaeac93e23e202771a6e567a8ea7e9a7cc690b87d8b1950e3da6"},
|
||||
{file = "grpcio_tools-1.36.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:ff1792b188183e977e2feccb1f3b3d4580f921df8f61385d7ae8eace10578a23"},
|
||||
{file = "grpcio_tools-1.36.1-cp35-cp35m-linux_armv7l.whl", hash = "sha256:a3a64797840fd4917ec98532d17b9b7c6a954dcfd7862657c750255556d369a5"},
|
||||
{file = "grpcio_tools-1.36.1-cp35-cp35m-macosx_10_10_intel.whl", hash = "sha256:0873697064cdbb116ba9f88ff524e13e9afd78bf7905ecd6a0f0f743bf40ca64"},
|
||||
{file = "grpcio_tools-1.36.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6df49b402f387decaaf57784c3e74bea6f34cf446cc45d4bf7b9adb34f97fb20"},
|
||||
{file = "grpcio_tools-1.36.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:aec997dafa90a29b399bdb23d968ab43da223faeac005d384a1194f43ee0f46e"},
|
||||
{file = "grpcio_tools-1.36.1-cp35-cp35m-manylinux2014_i686.whl", hash = "sha256:92336c60db1052c865ab7c9936680187d16d2f565c470ba03199e817120714e8"},
|
||||
{file = "grpcio_tools-1.36.1-cp35-cp35m-manylinux2014_x86_64.whl", hash = "sha256:bbe8da70ccbe21c72599eb8de5ad26bd053c01f4f03c48ea16323f96f1ec7095"},
|
||||
{file = "grpcio_tools-1.36.1-cp35-cp35m-win32.whl", hash = "sha256:96e1c0d267eb03b819a31bcf973579220ec3b8b53178952daa9e2f1ad696783f"},
|
||||
{file = "grpcio_tools-1.36.1-cp35-cp35m-win_amd64.whl", hash = "sha256:f4326b1a5352e85480629bf888b132f0aec79bb791d29cd3e2322586cd70433a"},
|
||||
{file = "grpcio_tools-1.36.1-cp36-cp36m-linux_armv7l.whl", hash = "sha256:f2befead0395e8aaab1e8f76825c8c9fa93d69249a513c26107a55183f91ccd9"},
|
||||
{file = "grpcio_tools-1.36.1-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:125859be6458e65e348c50ddb7a964ba48945d521af3f46ce35aca9a2b752296"},
|
||||
{file = "grpcio_tools-1.36.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:c669f1ee5642631ad93fa51d298306124d26bccc76ce63a3bc143ddcf01c58af"},
|
||||
{file = "grpcio_tools-1.36.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:693dc16a65b1766037fca8cddc173c0f45e79dd14e05d61128a30dbfd02f6503"},
|
||||
{file = "grpcio_tools-1.36.1-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:add07eb3c79478b003ac3af7b636275c37fa6bac56e6a29f79128bea09b37488"},
|
||||
{file = "grpcio_tools-1.36.1-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:747b547c487231a6325eda820d1d6a7c6080eda2cd1f68a7d4b2f8d9cc0a3e95"},
|
||||
{file = "grpcio_tools-1.36.1-cp36-cp36m-win32.whl", hash = "sha256:fd5eed43f5764209a95a58db82c064c1958525f30ad8ebb57df38dd2c9e86aa7"},
|
||||
{file = "grpcio_tools-1.36.1-cp36-cp36m-win_amd64.whl", hash = "sha256:bc6257b5533c66143f4f084aea3ae52c1c01f99997a8b81d2259d0cf083176b5"},
|
||||
{file = "grpcio_tools-1.36.1-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:f35fad86d99743cc15fccf11ec74d8c9b76e997cd233dc1fd031457d3f0fd7fc"},
|
||||
{file = "grpcio_tools-1.36.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:dcdfe82237e7498eb49dd12751716c55d189a5e49b4bda0bb53f85acbe51bbb1"},
|
||||
{file = "grpcio_tools-1.36.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d6b3c868c6ac15a0e288d3a5380ad5f01802cbbed8645333e496fa31ecea19af"},
|
||||
{file = "grpcio_tools-1.36.1-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:6898776449485feedb6d0fd98d3a36c8882c32a5603b86b2511e2557ee765d40"},
|
||||
{file = "grpcio_tools-1.36.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:6fee070c18be66a282ceb56245191dabf80986aee333e74d2fdea58118b452d4"},
|
||||
{file = "grpcio_tools-1.36.1-cp37-cp37m-win32.whl", hash = "sha256:55ed5c5de883defacd899123ba5a9f0077b7fb87d8f1778cb5996b4391604447"},
|
||||
{file = "grpcio_tools-1.36.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f74f0c3eedc0de72c402e82bb1199ffe5e359ccdac70bf789d65444042a84f42"},
|
||||
{file = "grpcio_tools-1.36.1-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:93e3ba4696b69fc4356a0823ecddd8b29ebb1fba0571f27574b1182ef5c262f6"},
|
||||
{file = "grpcio_tools-1.36.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:e730845677e45c6829d212be6e4fb69768979c3b35b5884293be02a7f436e18c"},
|
||||
{file = "grpcio_tools-1.36.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f7ba8d631f8f5c089958285545bd9e307fd752cdd1fa31515a51cfc1e04b833d"},
|
||||
{file = "grpcio_tools-1.36.1-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:af392594ba30b5ee470b7538cf792df970e2097edc299685d8e0d76b2b1bef7b"},
|
||||
{file = "grpcio_tools-1.36.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:facda541209a0b0edfccf6a5b18ce344c4e90bc8950c995482c85936a23ba939"},
|
||||
{file = "grpcio_tools-1.36.1-cp38-cp38-win32.whl", hash = "sha256:9fa491aaccd455e3aec35d12bcef5dce307c674f08e98bbbf33bf6774e6e2ec5"},
|
||||
{file = "grpcio_tools-1.36.1-cp38-cp38-win_amd64.whl", hash = "sha256:76900dde111192900c6eb5ed491cf0d8a13403e502c74859f2e2c3116842668a"},
|
||||
{file = "grpcio_tools-1.36.1-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0a7b85758e44d9585f27fc7692b58e63952a2e9130cfbbd16defda8c2ffbb2ad"},
|
||||
{file = "grpcio_tools-1.36.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:9b8556e2938ef9437ef07d028b46198f299533497df878f96785502e6f74250d"},
|
||||
{file = "grpcio_tools-1.36.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:cd44135fb8b45acc79424e7354bb4548911a6202ca2fac384574099f8d998f06"},
|
||||
{file = "grpcio_tools-1.36.1-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:c02b5b6d185b1af86342381ddd1ad3d0482c4116e203e52a7145636fb1b2ad12"},
|
||||
{file = "grpcio_tools-1.36.1-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:377cd9b8d2098d2ced48d3dee466fd73fb19128aa0edc6f1799077cf4dbda606"},
|
||||
{file = "grpcio_tools-1.36.1-cp39-cp39-win32.whl", hash = "sha256:120bad5a3f3288ae8acd07d839a13d7873304ae35a1d717033295e90ed9bd8ac"},
|
||||
{file = "grpcio_tools-1.36.1-cp39-cp39-win_amd64.whl", hash = "sha256:5cec989d219164312bdfa9389aedaea5887fb8133bb1a247fcde5901775b5427"},
|
||||
{file = "grpcio-tools-1.40.0.tar.gz", hash = "sha256:d440f2bc089ff628618c536904d5bc39d0b44f7afdda4c4c1ecd15fcf385bfba"},
|
||||
{file = "grpcio_tools-1.40.0-cp35-cp35m-macosx_10_10_intel.whl", hash = "sha256:ba6cc607a9cbff6a45969f6eca730612f6413634cbf70303af56e3db66021f48"},
|
||||
{file = "grpcio_tools-1.40.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:0531233bfa6599412ffcb711ee02a3302fcc38bd1edc03b4e2443394eea22960"},
|
||||
{file = "grpcio_tools-1.40.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:e89722df3a263acd7bb84802e556f5e5616be4e5292b0b7551a740b93360743e"},
|
||||
{file = "grpcio_tools-1.40.0-cp35-cp35m-manylinux2014_i686.whl", hash = "sha256:cea28f8fc02fdae07501aa51bf8d89f972f5bcbd87ae56755d71b1b6f9140e4d"},
|
||||
{file = "grpcio_tools-1.40.0-cp35-cp35m-manylinux2014_x86_64.whl", hash = "sha256:664ad975e16be1f4c8bfba0c7c7e24c73b5b7c6cf74a3d18825beb720841f775"},
|
||||
{file = "grpcio_tools-1.40.0-cp35-cp35m-win32.whl", hash = "sha256:8aa8a081cd8ee094231d7e263266b731bcbedd4a05cd17a5084fc5bcc6cfd7fe"},
|
||||
{file = "grpcio_tools-1.40.0-cp35-cp35m-win_amd64.whl", hash = "sha256:45794ebdec5bf7e9623806def361e61ae83f3acda5a53ff9744c13b5657a055a"},
|
||||
{file = "grpcio_tools-1.40.0-cp36-cp36m-linux_armv7l.whl", hash = "sha256:4805382c2f57bf393c2f7ec3c89391790f606249bf03cd722a875d3ba46e1b60"},
|
||||
{file = "grpcio_tools-1.40.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:4b85c4e8037c382dacaf65defbdb4fafee5884b614ef19e03f22026ae2c4735e"},
|
||||
{file = "grpcio_tools-1.40.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:849ccab9b043518fe38fe2c6c6bd4c6f6a87bad9184378f5269b0158b6d35453"},
|
||||
{file = "grpcio_tools-1.40.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:cf9d79ed7dea8363c7769324d87e35f2ac67f361c98ac960e45edd76b35df679"},
|
||||
{file = "grpcio_tools-1.40.0-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:224d427ff0aacbbdd353721f545340c99d16c204d77b638a688160b331943162"},
|
||||
{file = "grpcio_tools-1.40.0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:07e769dcf8938b5a355802d18b9bc0d7a7372276abce1b624e0af3b2b92e69c3"},
|
||||
{file = "grpcio_tools-1.40.0-cp36-cp36m-manylinux_2_24_aarch64.whl", hash = "sha256:ff9ae29f77f9061a9239a93c53535d7f43dfd82a1a08a96c26bf76a232c4fa46"},
|
||||
{file = "grpcio_tools-1.40.0-cp36-cp36m-win32.whl", hash = "sha256:00930e260a4900b68f8856c3813a32ec1db94666e140081831869fc92aedce46"},
|
||||
{file = "grpcio_tools-1.40.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5f026c6a73476ac1506d8a4c9aa33aaecdc6ac103ed592c98e9b6cc7a123f714"},
|
||||
{file = "grpcio_tools-1.40.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:d540128b3f4cc3a6ec16f0d48019dfa325e979e6a9d96c83979d7dd0adbf50c2"},
|
||||
{file = "grpcio_tools-1.40.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:fe262f5cc30eff3bd5a0ca93883a7fe9b1ade5c29282b86714140d6d084ab500"},
|
||||
{file = "grpcio_tools-1.40.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:9eb0926a21eff5a0e4444a35309a8d2fc18bb53242253f66e5a465a6adecaf48"},
|
||||
{file = "grpcio_tools-1.40.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:986dc7a49e6dc2ad2f54cb6637d18c27d2feb96e32a904cc7e861750bda1b46d"},
|
||||
{file = "grpcio_tools-1.40.0-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:168c4444f44e386d8b387825cee09278f9311ca125c32207d54c498934ce9bf0"},
|
||||
{file = "grpcio_tools-1.40.0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:bd212d363909b36e32227e0bf734e49909d4e8ba8ddff934b54649e5ba6ef938"},
|
||||
{file = "grpcio_tools-1.40.0-cp37-cp37m-manylinux_2_24_aarch64.whl", hash = "sha256:01a3652601ff39027083c004fc8662beebc40ce92e6d77ed96403d3980190508"},
|
||||
{file = "grpcio_tools-1.40.0-cp37-cp37m-win32.whl", hash = "sha256:1f3f5d931815059f179c529df3dfd61040e39894f21bcc4c412f35cbefceff15"},
|
||||
{file = "grpcio_tools-1.40.0-cp37-cp37m-win_amd64.whl", hash = "sha256:05e10caf77717c59bb70282a8c7013db6ae9c1fbc3fbdf14f7d84dfd5ff18f24"},
|
||||
{file = "grpcio_tools-1.40.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:47348f4f61a3a73e5304d096d22412d0ba00738307a5be0793930225c4034c18"},
|
||||
{file = "grpcio_tools-1.40.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:6b4235450b51f73db83237fdf54cca1235257eb8a84da0784513352f884bbb59"},
|
||||
{file = "grpcio_tools-1.40.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:791084528308a9ad403801527eb79074bb45cf71321204bdb12bcd58fcd810f8"},
|
||||
{file = "grpcio_tools-1.40.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4235c3ed899c44a8b528b07a8d98261b369d2072445cea0759310dcb9be5c329"},
|
||||
{file = "grpcio_tools-1.40.0-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:47c9e396636a64198d4fcdc9bc8d8ed8835bcc5cca28786f5f24de9feddbd937"},
|
||||
{file = "grpcio_tools-1.40.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:1d1065d74bbfd955cdacdfc88f4e728d651ccf546c8f2703ab4de4fb864bf073"},
|
||||
{file = "grpcio_tools-1.40.0-cp38-cp38-manylinux_2_24_aarch64.whl", hash = "sha256:27c10291fc41465e7931a214b6fbede38a497af766bab71fcbb9851f67a2c49f"},
|
||||
{file = "grpcio_tools-1.40.0-cp38-cp38-win32.whl", hash = "sha256:aa681d1986c112d716586bcd51a6bb1ac459fa8a74975e9c8070123c39731221"},
|
||||
{file = "grpcio_tools-1.40.0-cp38-cp38-win_amd64.whl", hash = "sha256:ce75b5c56439f6d438f67a0c9dfeec9b4295628447a98e7fe921e26bb321a50e"},
|
||||
{file = "grpcio_tools-1.40.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:93372ee8fc6e74af1db7128042ca21d8ceaa32b5de9cff73bed8dfaebdaac7ed"},
|
||||
{file = "grpcio_tools-1.40.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:f3a4bcc8bd57dbb53ec97634c5db9e459076dba8baf2c5d3a7e9e59397f0ee83"},
|
||||
{file = "grpcio_tools-1.40.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:687ec1aa210ac6bdbf7880b34f5f61bc85c3b25db2fc323a946ff570a6a984ea"},
|
||||
{file = "grpcio_tools-1.40.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:7f6ab2a9ccf50cf20e7be48531bf675b41b153cc1dcfe530c3fcbd430c44bbc9"},
|
||||
{file = "grpcio_tools-1.40.0-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:a36c8ebeb348ee95376f2cf10d7530ae34593ab1132f7215e867a9e82ed8117f"},
|
||||
{file = "grpcio_tools-1.40.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:f5eea943c0524a59113dd2747a13de17a7550e838f59e3fb09c1b4fa131efe27"},
|
||||
{file = "grpcio_tools-1.40.0-cp39-cp39-manylinux_2_24_aarch64.whl", hash = "sha256:76063203802e9858ec35cb454b96673a1336a8ae9e7313cc8de1a66ce3c357cf"},
|
||||
{file = "grpcio_tools-1.40.0-cp39-cp39-win32.whl", hash = "sha256:0d707e34dd86b4368391df6bbf9f7cb3649517f10655bc9ef4bc638a9c88caa5"},
|
||||
{file = "grpcio_tools-1.40.0-cp39-cp39-win_amd64.whl", hash = "sha256:fe6a3ee6667c44432859ee151da766f32fc01f91228ea5a529ae121ba44fef6f"},
|
||||
]
|
||||
grpclib = [
|
||||
{file = "grpclib-0.4.1.tar.gz", hash = "sha256:8c0021cd038634c268249e4cd168d9f3570e66ceceec1c9416094b788ebc8372"},
|
||||
@ -1345,26 +1338,27 @@ poethepoet = [
|
||||
{file = "poethepoet-0.10.0.tar.gz", hash = "sha256:70b97cb194b978dc464c70793e85e6f746cddf82b84a38bfb135946ad71ae19c"},
|
||||
]
|
||||
protobuf = [
|
||||
{file = "protobuf-3.15.7-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a14141d5c967362d2eedff8825d2b69cc36a5b3ed6b1f618557a04e58a3cf787"},
|
||||
{file = "protobuf-3.15.7-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d54d78f621852ec4fdd1484d1263ca04d4bf5ffdf7abffdbb939e444b6ff3385"},
|
||||
{file = "protobuf-3.15.7-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:462085acdb410b06335315fe7e63cb281a1902856e0f4657f341c283cedc1d56"},
|
||||
{file = "protobuf-3.15.7-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:849c92ce112e1ef648705c29ce044248e350f71d9d54a2026830623198f0bd38"},
|
||||
{file = "protobuf-3.15.7-cp35-cp35m-win32.whl", hash = "sha256:1f6083382f7714700deadf3014e921711e2f807de7f27e40c32b744701ae5b99"},
|
||||
{file = "protobuf-3.15.7-cp35-cp35m-win_amd64.whl", hash = "sha256:e17f60f00081adcb32068ee0bb51e418f6474acf83424244ff3512ffd2166385"},
|
||||
{file = "protobuf-3.15.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c75e563c6fb2ca5b8f21dd75c15659aa2c4a0025b9da3a7711ae661cd6a488d"},
|
||||
{file = "protobuf-3.15.7-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d939f41b4108350841c4790ebbadb61729e1363522fdb8434eb4e6f2065d0db1"},
|
||||
{file = "protobuf-3.15.7-cp36-cp36m-win32.whl", hash = "sha256:24f14c09d4c0a3641f1b0e9b552d026361de65b01686fdd3e5fdf8f9512cd79b"},
|
||||
{file = "protobuf-3.15.7-cp36-cp36m-win_amd64.whl", hash = "sha256:1247170191bcb2a8d978d11a58afe391004ec6c2184e4d961baf8102d43ff500"},
|
||||
{file = "protobuf-3.15.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:364cadaeec0756afdc099cbd88cb5659bd1bb7d547168d063abcb0272ccbb2f6"},
|
||||
{file = "protobuf-3.15.7-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0c3a6941b1e6e6e22d812a8e5c46bfe83082ea60d262a46f2cfb22d9b9fb17db"},
|
||||
{file = "protobuf-3.15.7-cp37-cp37m-win32.whl", hash = "sha256:eb5668f3f6a83b6603ca2e09be5b20de89521ea5914aabe032cce981e4129cc8"},
|
||||
{file = "protobuf-3.15.7-cp37-cp37m-win_amd64.whl", hash = "sha256:1001e671cf8476edce7fb72778358d026390649cc35a79d47b2a291684ccfbb2"},
|
||||
{file = "protobuf-3.15.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a5ba7dd6f97964655aa7b234c95d80886425a31b7010764f042cdeb985314d18"},
|
||||
{file = "protobuf-3.15.7-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:46674bd6fcf8c63b4b9869ba579685db67cf51ae966443dd6bd9a8fa00fcef62"},
|
||||
{file = "protobuf-3.15.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4c4399156fb27e3768313b7a59352c861a893252bda6fb9f3643beb3ebb7047e"},
|
||||
{file = "protobuf-3.15.7-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:85cd29faf056036167d87445d5a5059034c298881c044e71a73d3b61a4be1c23"},
|
||||
{file = "protobuf-3.15.7-py2.py3-none-any.whl", hash = "sha256:22054432b923c0086f9cf1e1c0c52d39bf3c6e31014ea42eec2dabc22ee26d78"},
|
||||
{file = "protobuf-3.15.7.tar.gz", hash = "sha256:2d03fc2591543cd2456d0b72230b50c4519546a8d379ac6fd3ecd84c6df61e5d"},
|
||||
{file = "protobuf-3.18.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9072cb18fca8998b77f969fb74d25a11d7f4a39a8b1ddc3cf76cd5abda8499cb"},
|
||||
{file = "protobuf-3.18.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f589346b5b3f702c1d30e2343c9897e6c35e7bd495c10a0e17d11ecb5ee5bd06"},
|
||||
{file = "protobuf-3.18.0-cp36-cp36m-win32.whl", hash = "sha256:93c077fd83879cf48f327a2491c24da447a09da6a7ab3cc311a6f5a61fcb5de0"},
|
||||
{file = "protobuf-3.18.0-cp36-cp36m-win_amd64.whl", hash = "sha256:3b5b81bb665aac548b413480f4e0d8c38a74bc4dea57835f288a3ce74f63dfe9"},
|
||||
{file = "protobuf-3.18.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d11465040cadcea8ecf5f0b131af5099a9696f9d0bef6f88148b372bacc1c52d"},
|
||||
{file = "protobuf-3.18.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:f6138462643adce0ed6e49007a63b7fd7dc4fda1ef4e15a70fcebe76c1407a71"},
|
||||
{file = "protobuf-3.18.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:877664b1b8d1e23553634f625e4e12aae4ff16cbbef473f8118c239d478f422a"},
|
||||
{file = "protobuf-3.18.0-cp37-cp37m-win32.whl", hash = "sha256:5201333b7aa711965c5769b250f8565a9924e8e27f8b622bbc5e6847aeaab1b1"},
|
||||
{file = "protobuf-3.18.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f3ecec3038c2fb4dad952d3d6cb9ca301999903a09e43794fb348da48f7577f"},
|
||||
{file = "protobuf-3.18.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:17181fc0814655812aac108e755bd5185d71aa8d81bd241cec6e232c84097918"},
|
||||
{file = "protobuf-3.18.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7646c20605fbee57e77fdbc4a90175538281b152f46ba17019916593f8062c2a"},
|
||||
{file = "protobuf-3.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80b0a5157f3a53043daf8eb7cfa1220b27a5a63dd6655dbd8e1e6f7b5dcd6347"},
|
||||
{file = "protobuf-3.18.0-cp38-cp38-win32.whl", hash = "sha256:5730de255c95b3403eedd1a568eb28203b913b6192ff5a3fdc3ff30f37107a38"},
|
||||
{file = "protobuf-3.18.0-cp38-cp38-win_amd64.whl", hash = "sha256:9147565f93e6699d7512747766598afe63205f226ac7b61f47954974c9aab852"},
|
||||
{file = "protobuf-3.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:568c049ff002a7523ed33fb612e6b97da002bf87ffb619a1fc3eadf2257a3b31"},
|
||||
{file = "protobuf-3.18.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:7e791a94db391ae22b3943fc88f6ba0e1f62b6ad58b33db7517df576c7834d23"},
|
||||
{file = "protobuf-3.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42c04e66ec5a38ad2171639dc9860c2f9594668f709ea3a4a192acf7346853a7"},
|
||||
{file = "protobuf-3.18.0-cp39-cp39-win32.whl", hash = "sha256:0a59ea8da307118372750e2fdfe0961622e675b8dd35e05c42384d618189a938"},
|
||||
{file = "protobuf-3.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:f7c8193ec805324ff6024242b00f64a24b94d56b895f62bf28a9d72a228d4fca"},
|
||||
{file = "protobuf-3.18.0-py2.py3-none-any.whl", hash = "sha256:615099e52e9fbc9fde00177267a94ca820ecf4e80093e390753568b7d8cb3c1a"},
|
||||
{file = "protobuf-3.18.0.tar.gz", hash = "sha256:18b308946a592e245299391e53c01b5b8efc2794f49986e80f37d7b5e60a270f"},
|
||||
]
|
||||
py = [
|
||||
{file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
|
||||
|
@ -23,7 +23,7 @@ python-dateutil = "^2.8"
|
||||
asv = "^0.4.2"
|
||||
black = "^21.11b0"
|
||||
bpython = "^0.19"
|
||||
grpcio-tools = "^1.30.0"
|
||||
grpcio-tools = "^1.40.0"
|
||||
jinja2 = "^2.11.2"
|
||||
mypy = "^0.770"
|
||||
poethepoet = ">=0.9.0"
|
||||
|
@ -145,6 +145,8 @@ class FieldMetadata:
|
||||
group: Optional[str] = None
|
||||
# Describes the wrapped type (e.g. when using google.protobuf.BoolValue)
|
||||
wraps: Optional[str] = None
|
||||
# Is the field optional
|
||||
optional: Optional[bool] = False
|
||||
|
||||
@staticmethod
|
||||
def get(field: dataclasses.Field) -> "FieldMetadata":
|
||||
@ -159,12 +161,15 @@ def dataclass_field(
|
||||
map_types: Optional[Tuple[str, str]] = None,
|
||||
group: Optional[str] = None,
|
||||
wraps: Optional[str] = None,
|
||||
optional: bool = False,
|
||||
) -> dataclasses.Field:
|
||||
"""Creates a dataclass field with attached protobuf metadata."""
|
||||
return dataclasses.field(
|
||||
default=PLACEHOLDER,
|
||||
default=None if optional else PLACEHOLDER,
|
||||
metadata={
|
||||
"betterproto": FieldMetadata(number, proto_type, map_types, group, wraps)
|
||||
"betterproto": FieldMetadata(
|
||||
number, proto_type, map_types, group, wraps, optional
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
@ -174,74 +179,107 @@ def dataclass_field(
|
||||
# out at runtime. The generated dataclass variables are still typed correctly.
|
||||
|
||||
|
||||
def enum_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_ENUM, group=group)
|
||||
def enum_field(number: int, group: Optional[str] = None, optional: bool = False) -> Any:
|
||||
return dataclass_field(number, TYPE_ENUM, group=group, optional=optional)
|
||||
|
||||
|
||||
def bool_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_BOOL, group=group)
|
||||
def bool_field(number: int, group: Optional[str] = None, optional: bool = False) -> Any:
|
||||
return dataclass_field(number, TYPE_BOOL, group=group, optional=optional)
|
||||
|
||||
|
||||
def int32_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_INT32, group=group)
|
||||
def int32_field(
|
||||
number: int, group: Optional[str] = None, optional: bool = False
|
||||
) -> Any:
|
||||
return dataclass_field(number, TYPE_INT32, group=group, optional=optional)
|
||||
|
||||
|
||||
def int64_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_INT64, group=group)
|
||||
def int64_field(
|
||||
number: int, group: Optional[str] = None, optional: bool = False
|
||||
) -> Any:
|
||||
return dataclass_field(number, TYPE_INT64, group=group, optional=optional)
|
||||
|
||||
|
||||
def uint32_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_UINT32, group=group)
|
||||
def uint32_field(
|
||||
number: int, group: Optional[str] = None, optional: bool = False
|
||||
) -> Any:
|
||||
return dataclass_field(number, TYPE_UINT32, group=group, optional=optional)
|
||||
|
||||
|
||||
def uint64_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_UINT64, group=group)
|
||||
def uint64_field(
|
||||
number: int, group: Optional[str] = None, optional: bool = False
|
||||
) -> Any:
|
||||
return dataclass_field(number, TYPE_UINT64, group=group, optional=optional)
|
||||
|
||||
|
||||
def sint32_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_SINT32, group=group)
|
||||
def sint32_field(
|
||||
number: int, group: Optional[str] = None, optional: bool = False
|
||||
) -> Any:
|
||||
return dataclass_field(number, TYPE_SINT32, group=group, optional=optional)
|
||||
|
||||
|
||||
def sint64_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_SINT64, group=group)
|
||||
def sint64_field(
|
||||
number: int, group: Optional[str] = None, optional: bool = False
|
||||
) -> Any:
|
||||
return dataclass_field(number, TYPE_SINT64, group=group, optional=optional)
|
||||
|
||||
|
||||
def float_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_FLOAT, group=group)
|
||||
def float_field(
|
||||
number: int, group: Optional[str] = None, optional: bool = False
|
||||
) -> Any:
|
||||
return dataclass_field(number, TYPE_FLOAT, group=group, optional=optional)
|
||||
|
||||
|
||||
def double_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_DOUBLE, group=group)
|
||||
def double_field(
|
||||
number: int, group: Optional[str] = None, optional: bool = False
|
||||
) -> Any:
|
||||
return dataclass_field(number, TYPE_DOUBLE, group=group, optional=optional)
|
||||
|
||||
|
||||
def fixed32_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_FIXED32, group=group)
|
||||
def fixed32_field(
|
||||
number: int, group: Optional[str] = None, optional: bool = False
|
||||
) -> Any:
|
||||
return dataclass_field(number, TYPE_FIXED32, group=group, optional=optional)
|
||||
|
||||
|
||||
def fixed64_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_FIXED64, group=group)
|
||||
def fixed64_field(
|
||||
number: int, group: Optional[str] = None, optional: bool = False
|
||||
) -> Any:
|
||||
return dataclass_field(number, TYPE_FIXED64, group=group, optional=optional)
|
||||
|
||||
|
||||
def sfixed32_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_SFIXED32, group=group)
|
||||
def sfixed32_field(
|
||||
number: int, group: Optional[str] = None, optional: bool = False
|
||||
) -> Any:
|
||||
return dataclass_field(number, TYPE_SFIXED32, group=group, optional=optional)
|
||||
|
||||
|
||||
def sfixed64_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_SFIXED64, group=group)
|
||||
def sfixed64_field(
|
||||
number: int, group: Optional[str] = None, optional: bool = False
|
||||
) -> Any:
|
||||
return dataclass_field(number, TYPE_SFIXED64, group=group, optional=optional)
|
||||
|
||||
|
||||
def string_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_STRING, group=group)
|
||||
def string_field(
|
||||
number: int, group: Optional[str] = None, optional: bool = False
|
||||
) -> Any:
|
||||
return dataclass_field(number, TYPE_STRING, group=group, optional=optional)
|
||||
|
||||
|
||||
def bytes_field(number: int, group: Optional[str] = None) -> Any:
|
||||
return dataclass_field(number, TYPE_BYTES, group=group)
|
||||
def bytes_field(
|
||||
number: int, group: Optional[str] = None, optional: bool = False
|
||||
) -> Any:
|
||||
return dataclass_field(number, TYPE_BYTES, group=group, optional=optional)
|
||||
|
||||
|
||||
def message_field(
|
||||
number: int, group: Optional[str] = None, wraps: Optional[str] = None
|
||||
number: int,
|
||||
group: Optional[str] = None,
|
||||
wraps: Optional[str] = None,
|
||||
optional: bool = False,
|
||||
) -> Any:
|
||||
return dataclass_field(number, TYPE_MESSAGE, group=group, wraps=wraps)
|
||||
return dataclass_field(
|
||||
number, TYPE_MESSAGE, group=group, wraps=wraps, optional=optional
|
||||
)
|
||||
|
||||
|
||||
def map_field(
|
||||
@ -586,7 +624,8 @@ class Message(ABC):
|
||||
if meta.group:
|
||||
group_current.setdefault(meta.group)
|
||||
|
||||
if self.__raw_get(field_name) != PLACEHOLDER:
|
||||
value = self.__raw_get(field_name)
|
||||
if value != PLACEHOLDER and not (meta.optional and value is None):
|
||||
# Found a non-sentinel value
|
||||
all_sentinel = False
|
||||
|
||||
@ -701,12 +740,16 @@ class Message(ABC):
|
||||
|
||||
if value is None:
|
||||
# Optional items should be skipped. This is used for the Google
|
||||
# wrapper types.
|
||||
# wrapper types and proto3 field presence/optional fields.
|
||||
continue
|
||||
|
||||
# Being selected in a a group means this field is the one that is
|
||||
# currently set in a `oneof` group, so it must be serialized even
|
||||
# if the value is the default zero value.
|
||||
#
|
||||
# Note that proto3 field presence/optional fields are put in a
|
||||
# synthetic single-item oneof by protoc, which helps us ensure we
|
||||
# send the value even if the value is the default zero value.
|
||||
selected_in_group = (
|
||||
meta.group and self._group_current[meta.group] == field_name
|
||||
)
|
||||
@ -829,8 +872,9 @@ class Message(ABC):
|
||||
# This is some kind of list (repeated) field.
|
||||
return list
|
||||
elif t.__origin__ is Union and t.__args__[1] is type(None):
|
||||
# This is an optional (wrapped) field. For setting the default we
|
||||
# really don't care what kind of field it is.
|
||||
# This is an optional field (either wrapped, or using proto3
|
||||
# field presence). For setting the default we really don't care
|
||||
# what kind of field it is.
|
||||
return type(None)
|
||||
else:
|
||||
return t
|
||||
@ -1041,6 +1085,9 @@ class Message(ABC):
|
||||
]
|
||||
if value or include_default_values:
|
||||
output[cased_name] = value
|
||||
elif value is None:
|
||||
if include_default_values:
|
||||
output[cased_name] = value
|
||||
elif (
|
||||
value._serialized_on_wire
|
||||
or include_default_values
|
||||
@ -1066,6 +1113,9 @@ class Message(ABC):
|
||||
if meta.proto_type in INT_64_TYPES:
|
||||
if field_is_repeated:
|
||||
output[cased_name] = [str(n) for n in value]
|
||||
elif value is None:
|
||||
if include_default_values:
|
||||
output[cased_name] = value
|
||||
else:
|
||||
output[cased_name] = str(value)
|
||||
elif meta.proto_type == TYPE_BYTES:
|
||||
@ -1073,6 +1123,8 @@ class Message(ABC):
|
||||
output[cased_name] = [
|
||||
b64encode(b).decode("utf8") for b in value
|
||||
]
|
||||
elif value is None and include_default_values:
|
||||
output[cased_name] = value
|
||||
else:
|
||||
output[cased_name] = b64encode(value).decode("utf8")
|
||||
elif meta.proto_type == TYPE_ENUM:
|
||||
@ -1085,6 +1137,12 @@ class Message(ABC):
|
||||
else:
|
||||
# transparently upgrade single value to repeated
|
||||
output[cased_name] = [enum_class(value).name]
|
||||
elif value is None:
|
||||
if include_default_values:
|
||||
output[cased_name] = value
|
||||
elif meta.optional:
|
||||
enum_class = field_types[field_name].__args__[0]
|
||||
output[cased_name] = enum_class(value).name
|
||||
else:
|
||||
enum_class = field_types[field_name] # noqa
|
||||
output[cased_name] = enum_class(value).name
|
||||
@ -1141,6 +1199,9 @@ class Message(ABC):
|
||||
setattr(self, field_name, v)
|
||||
elif meta.wraps:
|
||||
setattr(self, field_name, value[key])
|
||||
elif v is None:
|
||||
cls = self._betterproto.cls_by_field[field_name]
|
||||
setattr(self, field_name, cls().from_dict(value[key]))
|
||||
else:
|
||||
# NOTE: `from_dict` mutates the underlying message, so no
|
||||
# assignment here is necessary.
|
||||
|
@ -6,6 +6,7 @@ from dataclasses import dataclass
|
||||
from typing import Dict, List
|
||||
|
||||
import betterproto
|
||||
from betterproto.grpc.grpclib_server import ServiceBase
|
||||
|
||||
|
||||
class Syntax(betterproto.Enum):
|
||||
@ -46,17 +47,6 @@ class FieldCardinality(betterproto.Enum):
|
||||
CARDINALITY_REPEATED = 3
|
||||
|
||||
|
||||
class NullValue(betterproto.Enum):
|
||||
"""
|
||||
`NullValue` is a singleton enumeration to represent the null value for the
|
||||
`Value` type union. The JSON representation for `NullValue` is JSON
|
||||
`null`.
|
||||
"""
|
||||
|
||||
# Null value.
|
||||
NULL_VALUE = 0
|
||||
|
||||
|
||||
class FieldDescriptorProtoType(betterproto.Enum):
|
||||
TYPE_DOUBLE = 1
|
||||
TYPE_FLOAT = 2
|
||||
@ -108,165 +98,15 @@ class MethodOptionsIdempotencyLevel(betterproto.Enum):
|
||||
IDEMPOTENT = 2
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Timestamp(betterproto.Message):
|
||||
class NullValue(betterproto.Enum):
|
||||
"""
|
||||
A Timestamp represents a point in time independent of any time zone or
|
||||
local calendar, encoded as a count of seconds and fractions of seconds at
|
||||
nanosecond resolution. The count is relative to an epoch at UTC midnight on
|
||||
January 1, 1970, in the proleptic Gregorian calendar which extends the
|
||||
Gregorian calendar backwards to year one. All minutes are 60 seconds long.
|
||||
Leap seconds are "smeared" so that no leap second table is needed for
|
||||
interpretation, using a [24-hour linear
|
||||
smear](https://developers.google.com/time/smear). The range is from
|
||||
0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By restricting to
|
||||
that range, we ensure that we can convert to and from [RFC
|
||||
3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. # Examples
|
||||
Example 1: Compute Timestamp from POSIX `time()`. Timestamp timestamp;
|
||||
timestamp.set_seconds(time(NULL)); timestamp.set_nanos(0); Example 2:
|
||||
Compute Timestamp from POSIX `gettimeofday()`. struct timeval tv;
|
||||
gettimeofday(&tv, NULL); Timestamp timestamp;
|
||||
timestamp.set_seconds(tv.tv_sec); timestamp.set_nanos(tv.tv_usec *
|
||||
1000); Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
|
||||
FILETIME ft; GetSystemTimeAsFileTime(&ft); UINT64 ticks =
|
||||
(((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; // A Windows
|
||||
tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z // is
|
||||
11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. Timestamp
|
||||
timestamp; timestamp.set_seconds((INT64) ((ticks / 10000000) -
|
||||
11644473600LL)); timestamp.set_nanos((INT32) ((ticks % 10000000) *
|
||||
100)); Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
|
||||
long millis = System.currentTimeMillis(); Timestamp timestamp =
|
||||
Timestamp.newBuilder().setSeconds(millis / 1000) .setNanos((int)
|
||||
((millis % 1000) * 1000000)).build(); Example 5: Compute Timestamp from
|
||||
current time in Python. timestamp = Timestamp()
|
||||
timestamp.GetCurrentTime() # JSON Mapping In JSON format, the Timestamp
|
||||
type is encoded as a string in the [RFC
|
||||
3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
|
||||
"{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is
|
||||
always expressed using four digits while {month}, {day}, {hour}, {min}, and
|
||||
{sec} are zero-padded to two digits each. The fractional seconds, which can
|
||||
go up to 9 digits (i.e. up to 1 nanosecond resolution), are optional. The
|
||||
"Z" suffix indicates the timezone ("UTC"); the timezone is required. A
|
||||
proto3 JSON serializer should always use UTC (as indicated by "Z") when
|
||||
printing the Timestamp type and a proto3 JSON parser should be able to
|
||||
accept both UTC and other timezones (as indicated by an offset). For
|
||||
example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
|
||||
January 15, 2017. In JavaScript, one can convert a Date object to this
|
||||
format using the standard [toISOString()](https://developer.mozilla.org/en-
|
||||
US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) method.
|
||||
In Python, a standard `datetime.datetime` object can be converted to this
|
||||
format using
|
||||
[`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
|
||||
with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
|
||||
can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
|
||||
http://www.joda.org/joda-
|
||||
time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D )
|
||||
to obtain a formatter capable of generating timestamps in this format.
|
||||
`NullValue` is a singleton enumeration to represent the null value for the
|
||||
`Value` type union. The JSON representation for `NullValue` is JSON
|
||||
`null`.
|
||||
"""
|
||||
|
||||
# Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must
|
||||
# be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.
|
||||
seconds: int = betterproto.int64_field(1)
|
||||
# Non-negative fractions of a second at nanosecond resolution. Negative
|
||||
# second values with fractions must still have non-negative nanos values that
|
||||
# count forward in time. Must be from 0 to 999,999,999 inclusive.
|
||||
nanos: int = betterproto.int32_field(2)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class FieldMask(betterproto.Message):
|
||||
"""
|
||||
`FieldMask` represents a set of symbolic field paths, for example:
|
||||
paths: "f.a" paths: "f.b.d" Here `f` represents a field in some root
|
||||
message, `a` and `b` fields in the message found in `f`, and `d` a field
|
||||
found in the message in `f.b`. Field masks are used to specify a subset of
|
||||
fields that should be returned by a get operation or modified by an update
|
||||
operation. Field masks also have a custom JSON encoding (see below). #
|
||||
Field Masks in Projections When used in the context of a projection, a
|
||||
response message or sub-message is filtered by the API to only contain
|
||||
those fields as specified in the mask. For example, if the mask in the
|
||||
previous example is applied to a response message as follows: f {
|
||||
a : 22 b { d : 1 x : 2 } y : 13 }
|
||||
z: 8 The result will not contain specific values for fields x,y and z
|
||||
(their value will be set to the default, and omitted in proto text output):
|
||||
f { a : 22 b { d : 1 } } A repeated field is
|
||||
not allowed except at the last position of a paths string. If a FieldMask
|
||||
object is not present in a get operation, the operation applies to all
|
||||
fields (as if a FieldMask of all fields had been specified). Note that a
|
||||
field mask does not necessarily apply to the top-level response message. In
|
||||
case of a REST get operation, the field mask applies directly to the
|
||||
response, but in case of a REST list operation, the mask instead applies to
|
||||
each individual message in the returned resource list. In case of a REST
|
||||
custom method, other definitions may be used. Where the mask applies will
|
||||
be clearly documented together with its declaration in the API. In any
|
||||
case, the effect on the returned resource/resources is required behavior
|
||||
for APIs. # Field Masks in Update Operations A field mask in update
|
||||
operations specifies which fields of the targeted resource are going to be
|
||||
updated. The API is required to only change the values of the fields as
|
||||
specified in the mask and leave the others untouched. If a resource is
|
||||
passed in to describe the updated values, the API ignores the values of all
|
||||
fields not covered by the mask. If a repeated field is specified for an
|
||||
update operation, new values will be appended to the existing repeated
|
||||
field in the target resource. Note that a repeated field is only allowed in
|
||||
the last position of a `paths` string. If a sub-message is specified in the
|
||||
last position of the field mask for an update operation, then new value
|
||||
will be merged into the existing sub-message in the target resource. For
|
||||
example, given the target message: f { b { d: 1
|
||||
x: 2 } c: [1] } And an update message: f { b {
|
||||
d: 10 } c: [2] } then if the field mask is: paths: ["f.b",
|
||||
"f.c"] then the result will be: f { b { d: 10 x:
|
||||
2 } c: [1, 2] } An implementation may provide options to
|
||||
override this default behavior for repeated and message fields. In order to
|
||||
reset a field's value to the default, the field must be in the mask and set
|
||||
to the default value in the provided resource. Hence, in order to reset all
|
||||
fields of a resource, provide a default instance of the resource and set
|
||||
all fields in the mask, or do not provide a mask as described below. If a
|
||||
field mask is not present on update, the operation applies to all fields
|
||||
(as if a field mask of all fields has been specified). Note that in the
|
||||
presence of schema evolution, this may mean that fields the client does not
|
||||
know and has therefore not filled into the request will be reset to their
|
||||
default. If this is unwanted behavior, a specific service may require a
|
||||
client to always specify a field mask, producing an error if not. As with
|
||||
get operations, the location of the resource which describes the updated
|
||||
values in the request message depends on the operation kind. In any case,
|
||||
the effect of the field mask is required to be honored by the API. ##
|
||||
Considerations for HTTP REST The HTTP kind of an update operation which
|
||||
uses a field mask must be set to PATCH instead of PUT in order to satisfy
|
||||
HTTP semantics (PUT must only be used for full updates). # JSON Encoding of
|
||||
Field Masks In JSON, a field mask is encoded as a single string where paths
|
||||
are separated by a comma. Fields name in each path are converted to/from
|
||||
lower-camel naming conventions. As an example, consider the following
|
||||
message declarations: message Profile { User user = 1;
|
||||
Photo photo = 2; } message User { string display_name = 1;
|
||||
string address = 2; } In proto a field mask for `Profile` may look as
|
||||
such: mask { paths: "user.display_name" paths: "photo"
|
||||
} In JSON, the same mask is represented as below: { mask:
|
||||
"user.displayName,photo" } # Field Masks and Oneof Fields Field masks
|
||||
treat fields in oneofs just as regular fields. Consider the following
|
||||
message: message SampleMessage { oneof test_oneof {
|
||||
string name = 4; SubMessage sub_message = 9; } } The
|
||||
field mask can be: mask { paths: "name" } Or: mask {
|
||||
paths: "sub_message" } Note that oneof type names ("test_oneof" in this
|
||||
case) cannot be used in paths. ## Field Mask Verification The
|
||||
implementation of any API method which has a FieldMask type field in the
|
||||
request should verify the included field paths, and return an
|
||||
`INVALID_ARGUMENT` error if any path is unmappable.
|
||||
"""
|
||||
|
||||
# The set of field mask paths.
|
||||
paths: List[str] = betterproto.string_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class SourceContext(betterproto.Message):
|
||||
"""
|
||||
`SourceContext` represents information about the source of a protobuf
|
||||
element, like the file in which it is defined.
|
||||
"""
|
||||
|
||||
# The path-qualified name of the .proto file that contained the associated
|
||||
# protobuf element. For example: `"google/protobuf/source_context.proto"`.
|
||||
file_name: str = betterproto.string_field(1)
|
||||
# Null value.
|
||||
NULL_VALUE = 0
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
@ -283,24 +123,25 @@ class Any(betterproto.Message):
|
||||
Example 3: Pack and unpack a message in Python. foo = Foo(...) any
|
||||
= Any() any.Pack(foo) ... if any.Is(Foo.DESCRIPTOR):
|
||||
any.Unpack(foo) ... Example 4: Pack and unpack a message in Go
|
||||
foo := &pb.Foo{...} any, err := ptypes.MarshalAny(foo) ...
|
||||
foo := &pb.Foo{} if err := ptypes.UnmarshalAny(any, foo); err != nil {
|
||||
... } The pack methods provided by protobuf library will by default
|
||||
use 'type.googleapis.com/full.type.name' as the type URL and the unpack
|
||||
methods only use the fully qualified type name after the last '/' in the
|
||||
type URL, for example "foo.bar.com/x/y.z" will yield type name "y.z". JSON
|
||||
==== The JSON representation of an `Any` value uses the regular
|
||||
representation of the deserialized, embedded message, with an additional
|
||||
field `@type` which contains the type URL. Example: package
|
||||
google.profile; message Person { string first_name = 1;
|
||||
string last_name = 2; } { "@type":
|
||||
"type.googleapis.com/google.profile.Person", "firstName": <string>,
|
||||
"lastName": <string> } If the embedded message type is well-known and
|
||||
has a custom JSON representation, that representation will be embedded
|
||||
adding a field `value` which holds the custom JSON in addition to the
|
||||
`@type` field. Example (for message [google.protobuf.Duration][]): {
|
||||
"@type": "type.googleapis.com/google.protobuf.Duration", "value":
|
||||
"1.212s" }
|
||||
foo := &pb.Foo{...} any, err := anypb.New(foo) if err != nil {
|
||||
... } ... foo := &pb.Foo{} if err :=
|
||||
any.UnmarshalTo(foo); err != nil { ... } The pack methods
|
||||
provided by protobuf library will by default use
|
||||
'type.googleapis.com/full.type.name' as the type URL and the unpack methods
|
||||
only use the fully qualified type name after the last '/' in the type URL,
|
||||
for example "foo.bar.com/x/y.z" will yield type name "y.z". JSON ==== The
|
||||
JSON representation of an `Any` value uses the regular representation of
|
||||
the deserialized, embedded message, with an additional field `@type` which
|
||||
contains the type URL. Example: package google.profile; message
|
||||
Person { string first_name = 1; string last_name = 2; }
|
||||
{ "@type": "type.googleapis.com/google.profile.Person",
|
||||
"firstName": <string>, "lastName": <string> } If the embedded
|
||||
message type is well-known and has a custom JSON representation, that
|
||||
representation will be embedded adding a field `value` which holds the
|
||||
custom JSON in addition to the `@type` field. Example (for message
|
||||
[google.protobuf.Duration][]): { "@type":
|
||||
"type.googleapis.com/google.protobuf.Duration", "value": "1.212s"
|
||||
}
|
||||
"""
|
||||
|
||||
# A URL/resource name that uniquely identifies the type of the serialized
|
||||
@ -327,6 +168,18 @@ class Any(betterproto.Message):
|
||||
value: bytes = betterproto.bytes_field(2)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class SourceContext(betterproto.Message):
|
||||
"""
|
||||
`SourceContext` represents information about the source of a protobuf
|
||||
element, like the file in which it is defined.
|
||||
"""
|
||||
|
||||
# The path-qualified name of the .proto file that contained the associated
|
||||
# protobuf element. For example: `"google/protobuf/source_context.proto"`.
|
||||
file_name: str = betterproto.string_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Type(betterproto.Message):
|
||||
"""A protocol buffer message type."""
|
||||
@ -510,7 +363,7 @@ class Mixin(betterproto.Message):
|
||||
implies that all methods in `AccessControl` are also declared with same
|
||||
name and request/response types in `Storage`. A documentation generator or
|
||||
annotation processor will see the effective `Storage.GetAcl` method after
|
||||
inherting documentation and annotations as follows: service Storage {
|
||||
inheriting documentation and annotations as follows: service Storage {
|
||||
// Get the underlying ACL object. rpc GetAcl(GetAclRequest) returns
|
||||
(Acl) { option (google.api.http).get = "/v2/{resource=**}:getAcl";
|
||||
} ... } Note how the version in the path pattern changed from
|
||||
@ -530,215 +383,6 @@ class Mixin(betterproto.Message):
|
||||
root: str = betterproto.string_field(2)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Duration(betterproto.Message):
|
||||
"""
|
||||
A Duration represents a signed, fixed-length span of time represented as a
|
||||
count of seconds and fractions of seconds at nanosecond resolution. It is
|
||||
independent of any calendar and concepts like "day" or "month". It is
|
||||
related to Timestamp in that the difference between two Timestamp values is
|
||||
a Duration and it can be added or subtracted from a Timestamp. Range is
|
||||
approximately +-10,000 years. # Examples Example 1: Compute Duration from
|
||||
two Timestamps in pseudo code. Timestamp start = ...; Timestamp end
|
||||
= ...; Duration duration = ...; duration.seconds = end.seconds -
|
||||
start.seconds; duration.nanos = end.nanos - start.nanos; if
|
||||
(duration.seconds < 0 && duration.nanos > 0) { duration.seconds += 1;
|
||||
duration.nanos -= 1000000000; } else if (duration.seconds > 0 &&
|
||||
duration.nanos < 0) { duration.seconds -= 1; duration.nanos +=
|
||||
1000000000; } Example 2: Compute Timestamp from Timestamp + Duration in
|
||||
pseudo code. Timestamp start = ...; Duration duration = ...;
|
||||
Timestamp end = ...; end.seconds = start.seconds + duration.seconds;
|
||||
end.nanos = start.nanos + duration.nanos; if (end.nanos < 0) {
|
||||
end.seconds -= 1; end.nanos += 1000000000; } else if (end.nanos
|
||||
>= 1000000000) { end.seconds += 1; end.nanos -= 1000000000;
|
||||
} Example 3: Compute Duration from datetime.timedelta in Python. td =
|
||||
datetime.timedelta(days=3, minutes=10) duration = Duration()
|
||||
duration.FromTimedelta(td) # JSON Mapping In JSON format, the Duration type
|
||||
is encoded as a string rather than an object, where the string ends in the
|
||||
suffix "s" (indicating seconds) and is preceded by the number of seconds,
|
||||
with nanoseconds expressed as fractional seconds. For example, 3 seconds
|
||||
with 0 nanoseconds should be encoded in JSON format as "3s", while 3
|
||||
seconds and 1 nanosecond should be expressed in JSON format as
|
||||
"3.000000001s", and 3 seconds and 1 microsecond should be expressed in JSON
|
||||
format as "3.000001s".
|
||||
"""
|
||||
|
||||
# Signed seconds of the span of time. Must be from -315,576,000,000 to
|
||||
# +315,576,000,000 inclusive. Note: these bounds are computed from: 60
|
||||
# sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
|
||||
seconds: int = betterproto.int64_field(1)
|
||||
# Signed fractions of a second at nanosecond resolution of the span of time.
|
||||
# Durations less than one second are represented with a 0 `seconds` field and
|
||||
# a positive or negative `nanos` field. For durations of one second or more,
|
||||
# a non-zero value for the `nanos` field must be of the same sign as the
|
||||
# `seconds` field. Must be from -999,999,999 to +999,999,999 inclusive.
|
||||
nanos: int = betterproto.int32_field(2)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Struct(betterproto.Message):
|
||||
"""
|
||||
`Struct` represents a structured data value, consisting of fields which map
|
||||
to dynamically typed values. In some languages, `Struct` might be supported
|
||||
by a native representation. For example, in scripting languages like JS a
|
||||
struct is represented as an object. The details of that representation are
|
||||
described together with the proto support for the language. The JSON
|
||||
representation for `Struct` is JSON object.
|
||||
"""
|
||||
|
||||
# Unordered map of dynamically typed values.
|
||||
fields: Dict[str, "Value"] = betterproto.map_field(
|
||||
1, betterproto.TYPE_STRING, betterproto.TYPE_MESSAGE
|
||||
)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Value(betterproto.Message):
|
||||
"""
|
||||
`Value` represents a dynamically typed value which can be either null, a
|
||||
number, a string, a boolean, a recursive struct value, or a list of values.
|
||||
A producer of value is expected to set one of that variants, absence of any
|
||||
variant indicates an error. The JSON representation for `Value` is JSON
|
||||
value.
|
||||
"""
|
||||
|
||||
# Represents a null value.
|
||||
null_value: "NullValue" = betterproto.enum_field(1, group="kind")
|
||||
# Represents a double value.
|
||||
number_value: float = betterproto.double_field(2, group="kind")
|
||||
# Represents a string value.
|
||||
string_value: str = betterproto.string_field(3, group="kind")
|
||||
# Represents a boolean value.
|
||||
bool_value: bool = betterproto.bool_field(4, group="kind")
|
||||
# Represents a structured value.
|
||||
struct_value: "Struct" = betterproto.message_field(5, group="kind")
|
||||
# Represents a repeated `Value`.
|
||||
list_value: "ListValue" = betterproto.message_field(6, group="kind")
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class ListValue(betterproto.Message):
|
||||
"""
|
||||
`ListValue` is a wrapper around a repeated field of values. The JSON
|
||||
representation for `ListValue` is JSON array.
|
||||
"""
|
||||
|
||||
# Repeated field of dynamically typed values.
|
||||
values: List["Value"] = betterproto.message_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class DoubleValue(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `double`. The JSON representation for `DoubleValue` is
|
||||
JSON number.
|
||||
"""
|
||||
|
||||
# The double value.
|
||||
value: float = betterproto.double_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class FloatValue(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `float`. The JSON representation for `FloatValue` is
|
||||
JSON number.
|
||||
"""
|
||||
|
||||
# The float value.
|
||||
value: float = betterproto.float_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Int64Value(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `int64`. The JSON representation for `Int64Value` is
|
||||
JSON string.
|
||||
"""
|
||||
|
||||
# The int64 value.
|
||||
value: int = betterproto.int64_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class UInt64Value(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `uint64`. The JSON representation for `UInt64Value` is
|
||||
JSON string.
|
||||
"""
|
||||
|
||||
# The uint64 value.
|
||||
value: int = betterproto.uint64_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Int32Value(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `int32`. The JSON representation for `Int32Value` is
|
||||
JSON number.
|
||||
"""
|
||||
|
||||
# The int32 value.
|
||||
value: int = betterproto.int32_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class UInt32Value(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `uint32`. The JSON representation for `UInt32Value` is
|
||||
JSON number.
|
||||
"""
|
||||
|
||||
# The uint32 value.
|
||||
value: int = betterproto.uint32_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class BoolValue(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `bool`. The JSON representation for `BoolValue` is JSON
|
||||
`true` and `false`.
|
||||
"""
|
||||
|
||||
# The bool value.
|
||||
value: bool = betterproto.bool_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class StringValue(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `string`. The JSON representation for `StringValue` is
|
||||
JSON string.
|
||||
"""
|
||||
|
||||
# The string value.
|
||||
value: str = betterproto.string_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class BytesValue(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `bytes`. The JSON representation for `BytesValue` is
|
||||
JSON string.
|
||||
"""
|
||||
|
||||
# The bytes value.
|
||||
value: bytes = betterproto.bytes_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Empty(betterproto.Message):
|
||||
"""
|
||||
A generic empty message that you can re-use to avoid defining duplicated
|
||||
empty messages in your APIs. A typical example is to use it as the request
|
||||
or the response type of an API method. For instance: service Foo {
|
||||
rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); } The
|
||||
JSON representation for `Empty` is empty JSON object `{}`.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class FileDescriptorSet(betterproto.Message):
|
||||
"""
|
||||
@ -855,6 +499,23 @@ class FieldDescriptorProto(betterproto.Message):
|
||||
# camelCase.
|
||||
json_name: str = betterproto.string_field(10)
|
||||
options: "FieldOptions" = betterproto.message_field(8)
|
||||
# If true, this is a proto3 "optional". When a proto3 field is optional, it
|
||||
# tracks presence regardless of field type. When proto3_optional is true,
|
||||
# this field must be belong to a oneof to signal to old proto3 clients that
|
||||
# presence is tracked for this field. This oneof is known as a "synthetic"
|
||||
# oneof, and this field must be its sole member (each proto3 optional field
|
||||
# gets its own synthetic oneof). Synthetic oneofs exist in the descriptor
|
||||
# only, and do not generate any API. Synthetic oneofs must be ordered after
|
||||
# all "real" oneofs. For message fields, proto3_optional doesn't create any
|
||||
# semantic change, since non-repeated message fields always track presence.
|
||||
# However it still indicates the semantic detail of whether the user wrote
|
||||
# "optional" or not. This can be useful for round-tripping the .proto file.
|
||||
# For consistency we give message fields a synthetic oneof also, even though
|
||||
# it is not required to track presence. This is especially important because
|
||||
# the parser can't tell if a field is a message or an enum, so it must always
|
||||
# create a synthetic oneof. Proto2 optional fields do not set this flag,
|
||||
# because they already indicate optional with `LABEL_OPTIONAL`.
|
||||
proto3_optional: bool = betterproto.bool_field(17)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
@ -937,17 +598,18 @@ class FileOptions(betterproto.Message):
|
||||
# inappropriate because proto packages do not normally start with backwards
|
||||
# domain names.
|
||||
java_package: str = betterproto.string_field(1)
|
||||
# If set, all the classes from the .proto file are wrapped in a single outer
|
||||
# class with the given name. This applies to both Proto1 (equivalent to the
|
||||
# old "--one_java_file" option) and Proto2 (where a .proto always translates
|
||||
# to a single class, but you may want to explicitly choose the class name).
|
||||
# Controls the name of the wrapper Java class generated for the .proto file.
|
||||
# That class will always contain the .proto file's getDescriptor() method as
|
||||
# well as any top-level extensions defined in the .proto file. If
|
||||
# java_multiple_files is disabled, then all the other classes from the .proto
|
||||
# file will be nested inside the single wrapper outer class.
|
||||
java_outer_classname: str = betterproto.string_field(8)
|
||||
# If set true, then the Java code generator will generate a separate .java
|
||||
# If enabled, then the Java code generator will generate a separate .java
|
||||
# file for each top-level message, enum, and service defined in the .proto
|
||||
# file. Thus, these types will *not* be nested inside the outer class named
|
||||
# by java_outer_classname. However, the outer class will still be generated
|
||||
# to contain the file's getDescriptor() method as well as any top-level
|
||||
# extensions defined in the file.
|
||||
# file. Thus, these types will *not* be nested inside the wrapper class
|
||||
# named by java_outer_classname. However, the wrapper class will still be
|
||||
# generated to contain the file's getDescriptor() method as well as any top-
|
||||
# level extensions defined in the file.
|
||||
java_multiple_files: bool = betterproto.bool_field(10)
|
||||
# This option does nothing.
|
||||
java_generate_equals_and_hash: bool = betterproto.bool_field(20)
|
||||
@ -1315,3 +977,363 @@ class GeneratedCodeInfoAnnotation(betterproto.Message):
|
||||
# the identified offset. The end offset should be one past the last relevant
|
||||
# byte (so the length of the text = end - begin).
|
||||
end: int = betterproto.int32_field(4)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Duration(betterproto.Message):
|
||||
"""
|
||||
A Duration represents a signed, fixed-length span of time represented as a
|
||||
count of seconds and fractions of seconds at nanosecond resolution. It is
|
||||
independent of any calendar and concepts like "day" or "month". It is
|
||||
related to Timestamp in that the difference between two Timestamp values is
|
||||
a Duration and it can be added or subtracted from a Timestamp. Range is
|
||||
approximately +-10,000 years. # Examples Example 1: Compute Duration from
|
||||
two Timestamps in pseudo code. Timestamp start = ...; Timestamp end
|
||||
= ...; Duration duration = ...; duration.seconds = end.seconds -
|
||||
start.seconds; duration.nanos = end.nanos - start.nanos; if
|
||||
(duration.seconds < 0 && duration.nanos > 0) { duration.seconds += 1;
|
||||
duration.nanos -= 1000000000; } else if (duration.seconds > 0 &&
|
||||
duration.nanos < 0) { duration.seconds -= 1; duration.nanos +=
|
||||
1000000000; } Example 2: Compute Timestamp from Timestamp + Duration in
|
||||
pseudo code. Timestamp start = ...; Duration duration = ...;
|
||||
Timestamp end = ...; end.seconds = start.seconds + duration.seconds;
|
||||
end.nanos = start.nanos + duration.nanos; if (end.nanos < 0) {
|
||||
end.seconds -= 1; end.nanos += 1000000000; } else if (end.nanos
|
||||
>= 1000000000) { end.seconds += 1; end.nanos -= 1000000000;
|
||||
} Example 3: Compute Duration from datetime.timedelta in Python. td =
|
||||
datetime.timedelta(days=3, minutes=10) duration = Duration()
|
||||
duration.FromTimedelta(td) # JSON Mapping In JSON format, the Duration type
|
||||
is encoded as a string rather than an object, where the string ends in the
|
||||
suffix "s" (indicating seconds) and is preceded by the number of seconds,
|
||||
with nanoseconds expressed as fractional seconds. For example, 3 seconds
|
||||
with 0 nanoseconds should be encoded in JSON format as "3s", while 3
|
||||
seconds and 1 nanosecond should be expressed in JSON format as
|
||||
"3.000000001s", and 3 seconds and 1 microsecond should be expressed in JSON
|
||||
format as "3.000001s".
|
||||
"""
|
||||
|
||||
# Signed seconds of the span of time. Must be from -315,576,000,000 to
|
||||
# +315,576,000,000 inclusive. Note: these bounds are computed from: 60
|
||||
# sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
|
||||
seconds: int = betterproto.int64_field(1)
|
||||
# Signed fractions of a second at nanosecond resolution of the span of time.
|
||||
# Durations less than one second are represented with a 0 `seconds` field and
|
||||
# a positive or negative `nanos` field. For durations of one second or more,
|
||||
# a non-zero value for the `nanos` field must be of the same sign as the
|
||||
# `seconds` field. Must be from -999,999,999 to +999,999,999 inclusive.
|
||||
nanos: int = betterproto.int32_field(2)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Empty(betterproto.Message):
|
||||
"""
|
||||
A generic empty message that you can re-use to avoid defining duplicated
|
||||
empty messages in your APIs. A typical example is to use it as the request
|
||||
or the response type of an API method. For instance: service Foo {
|
||||
rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); } The
|
||||
JSON representation for `Empty` is empty JSON object `{}`.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class FieldMask(betterproto.Message):
|
||||
"""
|
||||
`FieldMask` represents a set of symbolic field paths, for example:
|
||||
paths: "f.a" paths: "f.b.d" Here `f` represents a field in some root
|
||||
message, `a` and `b` fields in the message found in `f`, and `d` a field
|
||||
found in the message in `f.b`. Field masks are used to specify a subset of
|
||||
fields that should be returned by a get operation or modified by an update
|
||||
operation. Field masks also have a custom JSON encoding (see below). #
|
||||
Field Masks in Projections When used in the context of a projection, a
|
||||
response message or sub-message is filtered by the API to only contain
|
||||
those fields as specified in the mask. For example, if the mask in the
|
||||
previous example is applied to a response message as follows: f {
|
||||
a : 22 b { d : 1 x : 2 } y : 13 }
|
||||
z: 8 The result will not contain specific values for fields x,y and z
|
||||
(their value will be set to the default, and omitted in proto text output):
|
||||
f { a : 22 b { d : 1 } } A repeated field is
|
||||
not allowed except at the last position of a paths string. If a FieldMask
|
||||
object is not present in a get operation, the operation applies to all
|
||||
fields (as if a FieldMask of all fields had been specified). Note that a
|
||||
field mask does not necessarily apply to the top-level response message. In
|
||||
case of a REST get operation, the field mask applies directly to the
|
||||
response, but in case of a REST list operation, the mask instead applies to
|
||||
each individual message in the returned resource list. In case of a REST
|
||||
custom method, other definitions may be used. Where the mask applies will
|
||||
be clearly documented together with its declaration in the API. In any
|
||||
case, the effect on the returned resource/resources is required behavior
|
||||
for APIs. # Field Masks in Update Operations A field mask in update
|
||||
operations specifies which fields of the targeted resource are going to be
|
||||
updated. The API is required to only change the values of the fields as
|
||||
specified in the mask and leave the others untouched. If a resource is
|
||||
passed in to describe the updated values, the API ignores the values of all
|
||||
fields not covered by the mask. If a repeated field is specified for an
|
||||
update operation, new values will be appended to the existing repeated
|
||||
field in the target resource. Note that a repeated field is only allowed in
|
||||
the last position of a `paths` string. If a sub-message is specified in the
|
||||
last position of the field mask for an update operation, then new value
|
||||
will be merged into the existing sub-message in the target resource. For
|
||||
example, given the target message: f { b { d: 1
|
||||
x: 2 } c: [1] } And an update message: f { b {
|
||||
d: 10 } c: [2] } then if the field mask is: paths: ["f.b",
|
||||
"f.c"] then the result will be: f { b { d: 10 x:
|
||||
2 } c: [1, 2] } An implementation may provide options to
|
||||
override this default behavior for repeated and message fields. In order to
|
||||
reset a field's value to the default, the field must be in the mask and set
|
||||
to the default value in the provided resource. Hence, in order to reset all
|
||||
fields of a resource, provide a default instance of the resource and set
|
||||
all fields in the mask, or do not provide a mask as described below. If a
|
||||
field mask is not present on update, the operation applies to all fields
|
||||
(as if a field mask of all fields has been specified). Note that in the
|
||||
presence of schema evolution, this may mean that fields the client does not
|
||||
know and has therefore not filled into the request will be reset to their
|
||||
default. If this is unwanted behavior, a specific service may require a
|
||||
client to always specify a field mask, producing an error if not. As with
|
||||
get operations, the location of the resource which describes the updated
|
||||
values in the request message depends on the operation kind. In any case,
|
||||
the effect of the field mask is required to be honored by the API. ##
|
||||
Considerations for HTTP REST The HTTP kind of an update operation which
|
||||
uses a field mask must be set to PATCH instead of PUT in order to satisfy
|
||||
HTTP semantics (PUT must only be used for full updates). # JSON Encoding of
|
||||
Field Masks In JSON, a field mask is encoded as a single string where paths
|
||||
are separated by a comma. Fields name in each path are converted to/from
|
||||
lower-camel naming conventions. As an example, consider the following
|
||||
message declarations: message Profile { User user = 1;
|
||||
Photo photo = 2; } message User { string display_name = 1;
|
||||
string address = 2; } In proto a field mask for `Profile` may look as
|
||||
such: mask { paths: "user.display_name" paths: "photo"
|
||||
} In JSON, the same mask is represented as below: { mask:
|
||||
"user.displayName,photo" } # Field Masks and Oneof Fields Field masks
|
||||
treat fields in oneofs just as regular fields. Consider the following
|
||||
message: message SampleMessage { oneof test_oneof {
|
||||
string name = 4; SubMessage sub_message = 9; } } The
|
||||
field mask can be: mask { paths: "name" } Or: mask {
|
||||
paths: "sub_message" } Note that oneof type names ("test_oneof" in this
|
||||
case) cannot be used in paths. ## Field Mask Verification The
|
||||
implementation of any API method which has a FieldMask type field in the
|
||||
request should verify the included field paths, and return an
|
||||
`INVALID_ARGUMENT` error if any path is unmappable.
|
||||
"""
|
||||
|
||||
# The set of field mask paths.
|
||||
paths: List[str] = betterproto.string_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Struct(betterproto.Message):
|
||||
"""
|
||||
`Struct` represents a structured data value, consisting of fields which map
|
||||
to dynamically typed values. In some languages, `Struct` might be supported
|
||||
by a native representation. For example, in scripting languages like JS a
|
||||
struct is represented as an object. The details of that representation are
|
||||
described together with the proto support for the language. The JSON
|
||||
representation for `Struct` is JSON object.
|
||||
"""
|
||||
|
||||
# Unordered map of dynamically typed values.
|
||||
fields: Dict[str, "Value"] = betterproto.map_field(
|
||||
1, betterproto.TYPE_STRING, betterproto.TYPE_MESSAGE
|
||||
)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Value(betterproto.Message):
|
||||
"""
|
||||
`Value` represents a dynamically typed value which can be either null, a
|
||||
number, a string, a boolean, a recursive struct value, or a list of values.
|
||||
A producer of value is expected to set one of that variants, absence of any
|
||||
variant indicates an error. The JSON representation for `Value` is JSON
|
||||
value.
|
||||
"""
|
||||
|
||||
# Represents a null value.
|
||||
null_value: "NullValue" = betterproto.enum_field(1, group="kind")
|
||||
# Represents a double value.
|
||||
number_value: float = betterproto.double_field(2, group="kind")
|
||||
# Represents a string value.
|
||||
string_value: str = betterproto.string_field(3, group="kind")
|
||||
# Represents a boolean value.
|
||||
bool_value: bool = betterproto.bool_field(4, group="kind")
|
||||
# Represents a structured value.
|
||||
struct_value: "Struct" = betterproto.message_field(5, group="kind")
|
||||
# Represents a repeated `Value`.
|
||||
list_value: "ListValue" = betterproto.message_field(6, group="kind")
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class ListValue(betterproto.Message):
|
||||
"""
|
||||
`ListValue` is a wrapper around a repeated field of values. The JSON
|
||||
representation for `ListValue` is JSON array.
|
||||
"""
|
||||
|
||||
# Repeated field of dynamically typed values.
|
||||
values: List["Value"] = betterproto.message_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Timestamp(betterproto.Message):
|
||||
"""
|
||||
A Timestamp represents a point in time independent of any time zone or
|
||||
local calendar, encoded as a count of seconds and fractions of seconds at
|
||||
nanosecond resolution. The count is relative to an epoch at UTC midnight on
|
||||
January 1, 1970, in the proleptic Gregorian calendar which extends the
|
||||
Gregorian calendar backwards to year one. All minutes are 60 seconds long.
|
||||
Leap seconds are "smeared" so that no leap second table is needed for
|
||||
interpretation, using a [24-hour linear
|
||||
smear](https://developers.google.com/time/smear). The range is from
|
||||
0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By restricting to
|
||||
that range, we ensure that we can convert to and from [RFC
|
||||
3339](https://www.ietf.org/rfc/rfc3339.txt) date strings. # Examples
|
||||
Example 1: Compute Timestamp from POSIX `time()`. Timestamp timestamp;
|
||||
timestamp.set_seconds(time(NULL)); timestamp.set_nanos(0); Example 2:
|
||||
Compute Timestamp from POSIX `gettimeofday()`. struct timeval tv;
|
||||
gettimeofday(&tv, NULL); Timestamp timestamp;
|
||||
timestamp.set_seconds(tv.tv_sec); timestamp.set_nanos(tv.tv_usec *
|
||||
1000); Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
|
||||
FILETIME ft; GetSystemTimeAsFileTime(&ft); UINT64 ticks =
|
||||
(((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; // A Windows
|
||||
tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z // is
|
||||
11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. Timestamp
|
||||
timestamp; timestamp.set_seconds((INT64) ((ticks / 10000000) -
|
||||
11644473600LL)); timestamp.set_nanos((INT32) ((ticks % 10000000) *
|
||||
100)); Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
|
||||
long millis = System.currentTimeMillis(); Timestamp timestamp =
|
||||
Timestamp.newBuilder().setSeconds(millis / 1000) .setNanos((int)
|
||||
((millis % 1000) * 1000000)).build(); Example 5: Compute Timestamp from
|
||||
Java `Instant.now()`. Instant now = Instant.now(); Timestamp
|
||||
timestamp = Timestamp.newBuilder().setSeconds(now.getEpochSecond())
|
||||
.setNanos(now.getNano()).build(); Example 6: Compute Timestamp from current
|
||||
time in Python. timestamp = Timestamp() timestamp.GetCurrentTime()
|
||||
# JSON Mapping In JSON format, the Timestamp type is encoded as a string in
|
||||
the [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the
|
||||
format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where
|
||||
{year} is always expressed using four digits while {month}, {day}, {hour},
|
||||
{min}, and {sec} are zero-padded to two digits each. The fractional
|
||||
seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),
|
||||
are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone
|
||||
is required. A proto3 JSON serializer should always use UTC (as indicated
|
||||
by "Z") when printing the Timestamp type and a proto3 JSON parser should be
|
||||
able to accept both UTC and other timezones (as indicated by an offset).
|
||||
For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC
|
||||
on January 15, 2017. In JavaScript, one can convert a Date object to this
|
||||
format using the standard [toISOString()](https://developer.mozilla.org/en-
|
||||
US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString) method.
|
||||
In Python, a standard `datetime.datetime` object can be converted to this
|
||||
format using
|
||||
[`strftime`](https://docs.python.org/2/library/time.html#time.strftime)
|
||||
with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one
|
||||
can use the Joda Time's [`ISODateTimeFormat.dateTime()`](
|
||||
http://www.joda.org/joda-
|
||||
time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime%2D%2D )
|
||||
to obtain a formatter capable of generating timestamps in this format.
|
||||
"""
|
||||
|
||||
# Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must
|
||||
# be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive.
|
||||
seconds: int = betterproto.int64_field(1)
|
||||
# Non-negative fractions of a second at nanosecond resolution. Negative
|
||||
# second values with fractions must still have non-negative nanos values that
|
||||
# count forward in time. Must be from 0 to 999,999,999 inclusive.
|
||||
nanos: int = betterproto.int32_field(2)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class DoubleValue(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `double`. The JSON representation for `DoubleValue` is
|
||||
JSON number.
|
||||
"""
|
||||
|
||||
# The double value.
|
||||
value: float = betterproto.double_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class FloatValue(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `float`. The JSON representation for `FloatValue` is
|
||||
JSON number.
|
||||
"""
|
||||
|
||||
# The float value.
|
||||
value: float = betterproto.float_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Int64Value(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `int64`. The JSON representation for `Int64Value` is
|
||||
JSON string.
|
||||
"""
|
||||
|
||||
# The int64 value.
|
||||
value: int = betterproto.int64_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class UInt64Value(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `uint64`. The JSON representation for `UInt64Value` is
|
||||
JSON string.
|
||||
"""
|
||||
|
||||
# The uint64 value.
|
||||
value: int = betterproto.uint64_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class Int32Value(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `int32`. The JSON representation for `Int32Value` is
|
||||
JSON number.
|
||||
"""
|
||||
|
||||
# The int32 value.
|
||||
value: int = betterproto.int32_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class UInt32Value(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `uint32`. The JSON representation for `UInt32Value` is
|
||||
JSON number.
|
||||
"""
|
||||
|
||||
# The uint32 value.
|
||||
value: int = betterproto.uint32_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class BoolValue(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `bool`. The JSON representation for `BoolValue` is JSON
|
||||
`true` and `false`.
|
||||
"""
|
||||
|
||||
# The bool value.
|
||||
value: bool = betterproto.bool_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class StringValue(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `string`. The JSON representation for `StringValue` is
|
||||
JSON string.
|
||||
"""
|
||||
|
||||
# The string value.
|
||||
value: str = betterproto.string_field(1)
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
class BytesValue(betterproto.Message):
|
||||
"""
|
||||
Wrapper message for `bytes`. The JSON representation for `BytesValue` is
|
||||
JSON string.
|
||||
"""
|
||||
|
||||
# The bytes value.
|
||||
value: bytes = betterproto.bytes_field(1)
|
||||
|
@ -5,6 +5,12 @@ from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
import betterproto
|
||||
from betterproto.grpc.grpclib_server import ServiceBase
|
||||
|
||||
|
||||
class CodeGeneratorResponseFeature(betterproto.Enum):
|
||||
FEATURE_NONE = 0
|
||||
FEATURE_PROTO3_OPTIONAL = 1
|
||||
|
||||
|
||||
@dataclass(eq=False, repr=False)
|
||||
@ -59,6 +65,9 @@ class CodeGeneratorResponse(betterproto.Message):
|
||||
# unparseable -- should be reported by writing a message to stderr and
|
||||
# exiting with a non-zero status code.
|
||||
error: str = betterproto.string_field(1)
|
||||
# A bitmask of supported features that the code generator supports. This is a
|
||||
# bitwise "or" of values from the Feature enum.
|
||||
supported_features: int = betterproto.uint64_field(2)
|
||||
file: List["CodeGeneratorResponseFile"] = betterproto.message_field(15)
|
||||
|
||||
|
||||
@ -108,6 +117,12 @@ class CodeGeneratorResponseFile(betterproto.Message):
|
||||
insertion_point: str = betterproto.string_field(2)
|
||||
# The file contents.
|
||||
content: str = betterproto.string_field(15)
|
||||
# Information describing the file content being inserted. If an insertion
|
||||
# point is used, this information will be appropriately offset and inserted
|
||||
# into the code generation metadata for the generated files.
|
||||
generated_code_info: "betterproto_lib_google_protobuf.GeneratedCodeInfo" = (
|
||||
betterproto.message_field(16)
|
||||
)
|
||||
|
||||
|
||||
import betterproto.lib.google.protobuf as betterproto_lib_google_protobuf
|
||||
|
@ -28,11 +28,8 @@ def main() -> None:
|
||||
if dump_file:
|
||||
dump_request(dump_file, request)
|
||||
|
||||
# Create response
|
||||
response = CodeGeneratorResponse()
|
||||
|
||||
# Generate code
|
||||
generate_code(request, response)
|
||||
response = generate_code(request)
|
||||
|
||||
# Serialise response message
|
||||
output = response.SerializeToString()
|
||||
|
@ -389,6 +389,8 @@ class FieldCompiler(MessageCompiler):
|
||||
args = []
|
||||
if self.field_wraps:
|
||||
args.append(f"wraps={self.field_wraps}")
|
||||
if self.optional:
|
||||
args.append(f"optional=True")
|
||||
return args
|
||||
|
||||
@property
|
||||
@ -444,6 +446,10 @@ class FieldCompiler(MessageCompiler):
|
||||
and not is_map(self.proto_obj, self.parent)
|
||||
)
|
||||
|
||||
@property
|
||||
def optional(self) -> bool:
|
||||
return self.proto_obj.proto3_optional
|
||||
|
||||
@property
|
||||
def mutable(self) -> bool:
|
||||
"""True if the field is a mutable type, otherwise False."""
|
||||
@ -463,6 +469,8 @@ class FieldCompiler(MessageCompiler):
|
||||
"""Python representation of the default proto value."""
|
||||
if self.repeated:
|
||||
return "[]"
|
||||
if self.optional:
|
||||
return "None"
|
||||
if self.py_type == "int":
|
||||
return "0"
|
||||
if self.py_type == "float":
|
||||
@ -530,6 +538,8 @@ class FieldCompiler(MessageCompiler):
|
||||
py_type = f"builtins.{py_type}"
|
||||
if self.repeated:
|
||||
return f"List[{py_type}]"
|
||||
if self.optional:
|
||||
return f"Optional[{py_type}]"
|
||||
return py_type
|
||||
|
||||
|
||||
|
@ -8,6 +8,7 @@ from betterproto.lib.google.protobuf import (
|
||||
from betterproto.lib.google.protobuf.compiler import (
|
||||
CodeGeneratorRequest,
|
||||
CodeGeneratorResponse,
|
||||
CodeGeneratorResponseFeature,
|
||||
CodeGeneratorResponseFile,
|
||||
)
|
||||
import itertools
|
||||
@ -60,10 +61,11 @@ def traverse(
|
||||
)
|
||||
|
||||
|
||||
def generate_code(
|
||||
request: CodeGeneratorRequest, response: CodeGeneratorResponse
|
||||
) -> None:
|
||||
def generate_code(request: CodeGeneratorRequest) -> CodeGeneratorResponse:
|
||||
response = CodeGeneratorResponse()
|
||||
|
||||
plugin_options = request.parameter.split(",") if request.parameter else []
|
||||
response.supported_features = CodeGeneratorResponseFeature.FEATURE_PROTO3_OPTIONAL
|
||||
|
||||
request_data = PluginRequestCompiler(plugin_request_obj=request)
|
||||
# Gather output packages
|
||||
@ -133,6 +135,8 @@ def generate_code(
|
||||
for output_package_name in sorted(output_paths.union(init_files)):
|
||||
print(f"Writing {output_package_name}", file=sys.stderr)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def read_protobuf_type(
|
||||
item: DescriptorProto,
|
||||
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"test1": 128,
|
||||
"test2": true,
|
||||
"test3": "A value",
|
||||
"test4": "aGVsbG8=",
|
||||
"test5": {
|
||||
"test": "Hello"
|
||||
},
|
||||
"test6": "B",
|
||||
"test7": "8589934592",
|
||||
"test8": 2.5
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
syntax = "proto3";
|
||||
|
||||
message InnerTest {
|
||||
string test = 1;
|
||||
}
|
||||
|
||||
message Test {
|
||||
optional uint32 test1 = 1;
|
||||
optional bool test2 = 2;
|
||||
optional string test3 = 3;
|
||||
optional bytes test4 = 4;
|
||||
optional InnerTest test5 = 5;
|
||||
optional TestEnum test6 = 6;
|
||||
optional uint64 test7 = 7;
|
||||
optional float test8 = 8;
|
||||
}
|
||||
|
||||
enum TestEnum {
|
||||
A = 0;
|
||||
B = 1;
|
||||
}
|
@ -0,0 +1 @@
|
||||
{}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"test1": 0,
|
||||
"test2": false,
|
||||
"test3": "",
|
||||
"test4": "",
|
||||
"test6": "A",
|
||||
"test7": "0",
|
||||
"test8": 0
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
import json
|
||||
|
||||
from tests.output_betterproto.proto3_field_presence import Test, InnerTest, TestEnum
|
||||
|
||||
|
||||
def test_null_fields_json():
|
||||
"""Ensure that using "null" in JSON is equivalent to not specifying a
|
||||
field, for fields with explicit presence"""
|
||||
|
||||
def test_json(ref_json: str, obj_json: str) -> None:
|
||||
"""`ref_json` and `obj_json` are JSON strings describing a `Test` object.
|
||||
Test that deserializing both leads to the same object, and that
|
||||
`ref_json` is the normalized format."""
|
||||
ref_obj = Test().from_json(ref_json)
|
||||
obj = Test().from_json(obj_json)
|
||||
|
||||
assert obj == ref_obj
|
||||
assert json.loads(obj.to_json(0)) == json.loads(ref_json)
|
||||
|
||||
test_json("{}", '{ "test1": null, "test2": null, "test3": null }')
|
||||
test_json("{}", '{ "test4": null, "test5": null, "test6": null }')
|
||||
test_json("{}", '{ "test7": null, "test8": null }')
|
||||
test_json('{ "test5": {} }', '{ "test3": null, "test5": {} }')
|
||||
|
||||
# Make sure that if include_default_values is set, None values are
|
||||
# exported.
|
||||
obj = Test()
|
||||
assert obj.to_dict() == {}
|
||||
assert obj.to_dict(include_default_values=True) == {
|
||||
"test1": None,
|
||||
"test2": None,
|
||||
"test3": None,
|
||||
"test4": None,
|
||||
"test5": None,
|
||||
"test6": None,
|
||||
"test7": None,
|
||||
"test8": None,
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"nested": {}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
syntax = "proto3";
|
||||
|
||||
message Test {
|
||||
oneof kind {
|
||||
Nested nested = 1;
|
||||
WithOptional with_optional = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message InnerNested {
|
||||
optional bool a = 1;
|
||||
}
|
||||
|
||||
message Nested {
|
||||
InnerNested inner = 1;
|
||||
}
|
||||
|
||||
message WithOptional {
|
||||
optional bool b = 2;
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
from tests.output_betterproto.proto3_field_presence_oneof import (
|
||||
Test,
|
||||
InnerNested,
|
||||
Nested,
|
||||
WithOptional,
|
||||
)
|
||||
|
||||
|
||||
def test_serialization():
|
||||
"""Ensure that serialization of fields unset but with explicit field
|
||||
presence do not bloat the serialized payload with length-delimited fields
|
||||
with length 0"""
|
||||
|
||||
def test_empty_nested(message: Test) -> None:
|
||||
# '0a' => tag 1, length delimited
|
||||
# '00' => length: 0
|
||||
assert bytes(message) == bytearray.fromhex("0a 00")
|
||||
|
||||
test_empty_nested(Test(nested=Nested()))
|
||||
test_empty_nested(Test(nested=Nested(inner=None)))
|
||||
test_empty_nested(Test(nested=Nested(inner=InnerNested(a=None))))
|
||||
|
||||
def test_empty_with_optional(message: Test) -> None:
|
||||
# '12' => tag 2, length delimited
|
||||
# '00' => length: 0
|
||||
assert bytes(message) == bytearray.fromhex("12 00")
|
||||
|
||||
test_empty_with_optional(Test(with_optional=WithOptional()))
|
||||
test_empty_with_optional(Test(with_optional=WithOptional(b=None)))
|
Loading…
x
Reference in New Issue
Block a user