From c4b25349363248412c9a5c34fc1e21433b8d9a78 Mon Sep 17 00:00:00 2001 From: roblabla Date: Mon, 20 Sep 2021 15:33:31 +0200 Subject: [PATCH 01/13] Update protobuf pregenerated files --- poetry.lock | 67 +- .../lib/google/protobuf/__init__.py | 888 +++++++++--------- .../lib/google/protobuf/compiler/__init__.py | 15 + 3 files changed, 512 insertions(+), 458 deletions(-) diff --git a/poetry.lock b/poetry.lock index f56b1e4a7..48e25e0fc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -452,15 +452,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" @@ -842,7 +839,7 @@ compiler = ["black", "jinja2"] [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "c0a0589a01ba432403b80c01c13fb9e139d75d6d2760fa074b723f773bd61518" +content-hash = "a48374616ea8f1f01ba21c9702c8b9d2bb2474471a6a7d173ded2f15e8d91135" [metadata.files] alabaster = [ @@ -1195,20 +1192,39 @@ markupsafe = [ {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0"}, + {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1"}, + {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"}, {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"}, {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032"}, + {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b"}, {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"}, {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"}, + {file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"}, {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, ] more-itertools = [ @@ -1295,26 +1311,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"}, diff --git a/src/betterproto/lib/google/protobuf/__init__.py b/src/betterproto/lib/google/protobuf/__init__.py index b361c04ee..e9b6de7b5 100644 --- a/src/betterproto/lib/google/protobuf/__init__.py +++ b/src/betterproto/lib/google/protobuf/__init__.py @@ -6,6 +6,7 @@ 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): - """ - 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. - """ - - # 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): +class NullValue(betterproto.Enum): """ - `SourceContext` represents information about the source of a protobuf - element, like the file in which it is defined. + `NullValue` is a singleton enumeration to represent the null value for the + `Value` type union. The JSON representation for `NullValue` is JSON + `null`. """ - # 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": , - "lastName": } 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": , "lastName": } 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 @@ -531,251 +384,42 @@ class Mixin(betterproto.Message): @dataclass(eq=False, repr=False) -class Duration(betterproto.Message): +class FileDescriptorSet(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". + The protocol compiler can output a FileDescriptorSet containing the .proto + files it parses. """ - # 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) + file: List["FileDescriptorProto"] = betterproto.message_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. - """ +class FileDescriptorProto(betterproto.Message): + """Describes a complete .proto file.""" - # 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): - """ - The protocol compiler can output a FileDescriptorSet containing the .proto - files it parses. - """ - - file: List["FileDescriptorProto"] = betterproto.message_field(1) - - -@dataclass(eq=False, repr=False) -class FileDescriptorProto(betterproto.Message): - """Describes a complete .proto file.""" - - name: str = betterproto.string_field(1) - package: str = betterproto.string_field(2) - # Names of files imported by this file. - dependency: List[str] = betterproto.string_field(3) - # Indexes of the public imported files in the dependency list above. - public_dependency: List[int] = betterproto.int32_field(10) - # Indexes of the weak imported files in the dependency list. For Google- - # internal migration only. Do not use. - weak_dependency: List[int] = betterproto.int32_field(11) - # All top-level definitions in this file. - message_type: List["DescriptorProto"] = betterproto.message_field(4) - enum_type: List["EnumDescriptorProto"] = betterproto.message_field(5) - service: List["ServiceDescriptorProto"] = betterproto.message_field(6) - extension: List["FieldDescriptorProto"] = betterproto.message_field(7) - options: "FileOptions" = betterproto.message_field(8) - # This field contains optional information about the original source code. - # You may safely remove this entire field without harming runtime - # functionality of the descriptors -- the information is needed only by - # development tools. - source_code_info: "SourceCodeInfo" = betterproto.message_field(9) - # The syntax of the proto file. The supported values are "proto2" and - # "proto3". - syntax: str = betterproto.string_field(12) + name: str = betterproto.string_field(1) + package: str = betterproto.string_field(2) + # Names of files imported by this file. + dependency: List[str] = betterproto.string_field(3) + # Indexes of the public imported files in the dependency list above. + public_dependency: List[int] = betterproto.int32_field(10) + # Indexes of the weak imported files in the dependency list. For Google- + # internal migration only. Do not use. + weak_dependency: List[int] = betterproto.int32_field(11) + # All top-level definitions in this file. + message_type: List["DescriptorProto"] = betterproto.message_field(4) + enum_type: List["EnumDescriptorProto"] = betterproto.message_field(5) + service: List["ServiceDescriptorProto"] = betterproto.message_field(6) + extension: List["FieldDescriptorProto"] = betterproto.message_field(7) + options: "FileOptions" = betterproto.message_field(8) + # This field contains optional information about the original source code. + # You may safely remove this entire field without harming runtime + # functionality of the descriptors -- the information is needed only by + # development tools. + source_code_info: "SourceCodeInfo" = betterproto.message_field(9) + # The syntax of the proto file. The supported values are "proto2" and + # "proto3". + syntax: str = betterproto.string_field(12) @dataclass(eq=False, repr=False) @@ -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) diff --git a/src/betterproto/lib/google/protobuf/compiler/__init__.py b/src/betterproto/lib/google/protobuf/compiler/__init__.py index e5c8b60f6..cb2396d27 100644 --- a/src/betterproto/lib/google/protobuf/compiler/__init__.py +++ b/src/betterproto/lib/google/protobuf/compiler/__init__.py @@ -5,6 +5,12 @@ 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 From ae39f00b94a6df5bf7b2332e561800d1fba4c083 Mon Sep 17 00:00:00 2001 From: roblabla Date: Tue, 21 Sep 2021 11:30:17 +0200 Subject: [PATCH 02/13] Update grpcio-tools to latest version --- poetry.lock | 190 ++++++++++++++++++++++++------------------------- pyproject.toml | 2 +- 2 files changed, 94 insertions(+), 98 deletions(-) diff --git a/poetry.lock b/poetry.lock index 48e25e0fc..a7aaaef4d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -223,7 +223,7 @@ docs = ["sphinx"] [[package]] name = "grpcio" -version = "1.36.1" +version = "1.40.0" description = "HTTP/2-based RPC framework" category = "dev" optional = false @@ -233,18 +233,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]] @@ -839,7 +839,7 @@ compiler = ["black", "jinja2"] [metadata] lock-version = "1.1" python-versions = "^3.6" -content-hash = "a48374616ea8f1f01ba21c9702c8b9d2bb2474471a6a7d173ded2f15e8d91135" +content-hash = "b92ffad38c0c0dbb3517741925fc626d7d92b5e2ad5ab7e01227421f0f3b3c6e" [metadata.files] alabaster = [ @@ -1043,100 +1043,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"}, diff --git a/pyproject.toml b/pyproject.toml index c13c84a2a..870f4f880 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ python-dateutil = "^2.8" asv = "^0.4.2" black = "^20.8b1" bpython = "^0.19" -grpcio-tools = "^1.30.0" +grpcio-tools = "^1.40.0" jinja2 = "^2.11.2" mypy = "^0.770" poethepoet = ">=0.9.0" From c0aca819edac6aad65f59660f261867dd5796bf0 Mon Sep 17 00:00:00 2001 From: roblabla Date: Tue, 21 Sep 2021 11:45:43 +0200 Subject: [PATCH 03/13] Implement proto3 field presence --- src/betterproto/__init__.py | 77 ++++++++++--------- src/betterproto/plugin/main.py | 5 +- src/betterproto/plugin/models.py | 12 +++ src/betterproto/plugin/parser.py | 9 ++- .../proto3_field_presence.json | 7 ++ .../proto3_field_presence.proto | 13 ++++ 6 files changed, 80 insertions(+), 43 deletions(-) create mode 100644 tests/inputs/proto3_field_presence/proto3_field_presence.json create mode 100644 tests/inputs/proto3_field_presence/proto3_field_presence.proto diff --git a/src/betterproto/__init__.py b/src/betterproto/__init__.py index c894affc9..f551e93c8 100644 --- a/src/betterproto/__init__.py +++ b/src/betterproto/__init__.py @@ -159,10 +159,11 @@ 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) }, @@ -174,74 +175,74 @@ 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( @@ -701,12 +702,16 @@ def __bytes__(self) -> bytes: 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 ) diff --git a/src/betterproto/plugin/main.py b/src/betterproto/plugin/main.py index e0b2557d2..8982321f1 100755 --- a/src/betterproto/plugin/main.py +++ b/src/betterproto/plugin/main.py @@ -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() diff --git a/src/betterproto/plugin/models.py b/src/betterproto/plugin/models.py index 46dc9fb28..21d0f3c74 100644 --- a/src/betterproto/plugin/models.py +++ b/src/betterproto/plugin/models.py @@ -383,6 +383,8 @@ def betterproto_field_args(self) -> List[str]: args = [] if self.field_wraps: args.append(f"wraps={self.field_wraps}") + if self.optional: + args.append(f"optional=True") return args @property @@ -431,6 +433,12 @@ def repeated(self) -> bool: and not is_map(self.proto_obj, self.parent) ) + @property + def optional(self) -> bool: + # TODO: Should proto2 optional fields with kind=Message also be + # considered Optional. + return self.proto_obj.proto3_optional + @property def mutable(self) -> bool: """True if the field is a mutable type, otherwise False.""" @@ -450,6 +458,8 @@ def default_value_string(self) -> Union[Text, None, float, int]: """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": @@ -506,6 +516,8 @@ def py_type(self) -> str: def annotation(self) -> str: if self.repeated: return f"List[{self.py_type}]" + if self.optional: + return f"Optional[{self.py_type}]" return self.py_type diff --git a/src/betterproto/plugin/parser.py b/src/betterproto/plugin/parser.py index 5e929d8de..e97275e1b 100644 --- a/src/betterproto/plugin/parser.py +++ b/src/betterproto/plugin/parser.py @@ -8,6 +8,7 @@ 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,7 @@ 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, diff --git a/tests/inputs/proto3_field_presence/proto3_field_presence.json b/tests/inputs/proto3_field_presence/proto3_field_presence.json new file mode 100644 index 000000000..76d31db91 --- /dev/null +++ b/tests/inputs/proto3_field_presence/proto3_field_presence.json @@ -0,0 +1,7 @@ +{ + "test1": null, + "test2": null, + "test3": null, + "test4": null, + "test5": null +} diff --git a/tests/inputs/proto3_field_presence/proto3_field_presence.proto b/tests/inputs/proto3_field_presence/proto3_field_presence.proto new file mode 100644 index 000000000..83d728a95 --- /dev/null +++ b/tests/inputs/proto3_field_presence/proto3_field_presence.proto @@ -0,0 +1,13 @@ +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; +} From 1bc9673769469aa17582b1d8e52b7f6e2d33feb7 Mon Sep 17 00:00:00 2001 From: roblabla Date: Tue, 21 Sep 2021 13:49:58 +0200 Subject: [PATCH 04/13] Fix to_dict with None optional fields. --- src/betterproto/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/betterproto/__init__.py b/src/betterproto/__init__.py index f551e93c8..e948776ea 100644 --- a/src/betterproto/__init__.py +++ b/src/betterproto/__init__.py @@ -1046,6 +1046,8 @@ def to_dict( ] if value or include_default_values: output[cased_name] = value + elif value is None: + output[cased_name] = None elif ( value._serialized_on_wire or include_default_values @@ -1078,6 +1080,8 @@ def to_dict( output[cased_name] = [ b64encode(b).decode("utf8") for b in value ] + elif value is None: + output[cased_name] = None else: output[cased_name] = b64encode(value).decode("utf8") elif meta.proto_type == TYPE_ENUM: @@ -1090,6 +1094,8 @@ def to_dict( else: # transparently upgrade single value to repeated output[cased_name] = [enum_class(value).name] + elif value is None: + output[cased_name] = None else: enum_class = field_types[field_name] # noqa output[cased_name] = enum_class(value).name From 9fff31dc0a932e6428c1ab42da588d7d8edb972b Mon Sep 17 00:00:00 2001 From: roblabla Date: Thu, 30 Sep 2021 15:17:16 +0200 Subject: [PATCH 05/13] Add test with optional enum --- .../proto3_field_presence.json | 13 ++++++++----- .../proto3_field_presence.proto | 6 ++++++ .../proto3_field_presence_default.json | 8 ++++++++ .../proto3_field_presence_missing.json | 8 ++++++++ 4 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 tests/inputs/proto3_field_presence/proto3_field_presence_default.json create mode 100644 tests/inputs/proto3_field_presence/proto3_field_presence_missing.json diff --git a/tests/inputs/proto3_field_presence/proto3_field_presence.json b/tests/inputs/proto3_field_presence/proto3_field_presence.json index 76d31db91..3ed977326 100644 --- a/tests/inputs/proto3_field_presence/proto3_field_presence.json +++ b/tests/inputs/proto3_field_presence/proto3_field_presence.json @@ -1,7 +1,10 @@ { - "test1": null, - "test2": null, - "test3": null, - "test4": null, - "test5": null + "test1": 128, + "test2": true, + "test3": "A value", + "test4": "aGVsbG8=", + "test5": { + "test": "Hello" + }, + "test6": "B" } diff --git a/tests/inputs/proto3_field_presence/proto3_field_presence.proto b/tests/inputs/proto3_field_presence/proto3_field_presence.proto index 83d728a95..1f33cb488 100644 --- a/tests/inputs/proto3_field_presence/proto3_field_presence.proto +++ b/tests/inputs/proto3_field_presence/proto3_field_presence.proto @@ -10,4 +10,10 @@ message Test { optional string test3 = 3; optional bytes test4 = 4; optional InnerTest test5 = 5; + optional TestEnum test6 = 6; +} + +enum TestEnum { + A = 0; + B = 1; } diff --git a/tests/inputs/proto3_field_presence/proto3_field_presence_default.json b/tests/inputs/proto3_field_presence/proto3_field_presence_default.json new file mode 100644 index 000000000..6d0880ead --- /dev/null +++ b/tests/inputs/proto3_field_presence/proto3_field_presence_default.json @@ -0,0 +1,8 @@ +{ + "test1": null, + "test2": null, + "test3": null, + "test4": null, + "test5": null, + "test6": null +} diff --git a/tests/inputs/proto3_field_presence/proto3_field_presence_missing.json b/tests/inputs/proto3_field_presence/proto3_field_presence_missing.json new file mode 100644 index 000000000..66156048d --- /dev/null +++ b/tests/inputs/proto3_field_presence/proto3_field_presence_missing.json @@ -0,0 +1,8 @@ +{ + "test1": 0, + "test2": false, + "test3": "", + "test4": "", + "test5": null, + "test6": "A" +} From 0373fdc91f34dbd18000439eb350559f446cfb3d Mon Sep 17 00:00:00 2001 From: roblabla Date: Thu, 30 Sep 2021 15:51:31 +0200 Subject: [PATCH 06/13] Properly support optional enums --- src/betterproto/__init__.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/betterproto/__init__.py b/src/betterproto/__init__.py index e948776ea..50df44434 100644 --- a/src/betterproto/__init__.py +++ b/src/betterproto/__init__.py @@ -834,8 +834,9 @@ def _get_field_default_gen(cls, field: dataclasses.Field) -> Any: # 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 @@ -1009,6 +1010,7 @@ def to_dict( defaults = self._betterproto.default_gen for field_name, meta in self._betterproto.meta_by_field_name.items(): field_is_repeated = defaults[field_name] is list + field_is_optional = defaults[field_name] is type(None) value = getattr(self, field_name) cased_name = casing(field_name).rstrip("_") # type: ignore if meta.proto_type == TYPE_MESSAGE: @@ -1096,6 +1098,9 @@ def to_dict( output[cased_name] = [enum_class(value).name] elif value is None: output[cased_name] = None + elif field_is_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 @@ -1133,6 +1138,9 @@ def from_dict(self: T, value: Dict[str, Any]) -> T: if value[key] is not None: if meta.proto_type == TYPE_MESSAGE: v = getattr(self, field_name) + if value[key] is None and self._get_field_default(key) == None: + # Setting an optional value to None. + setattr(self, field_name, None) if isinstance(v, list): cls = self._betterproto.cls_by_field[field_name] if cls == datetime: @@ -1152,6 +1160,9 @@ def from_dict(self: T, value: Dict[str, Any]) -> T: 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. From 286a6b9a48a4c3c6851a34844d3c1f0edcb056e6 Mon Sep 17 00:00:00 2001 From: roblabla Date: Thu, 30 Sep 2021 16:29:38 +0200 Subject: [PATCH 07/13] Add tests for 64-bit ints and floats --- tests/inputs/proto3_field_presence/proto3_field_presence.json | 4 +++- .../inputs/proto3_field_presence/proto3_field_presence.proto | 2 ++ .../proto3_field_presence/proto3_field_presence_default.json | 4 +++- .../proto3_field_presence/proto3_field_presence_missing.json | 4 +++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/inputs/proto3_field_presence/proto3_field_presence.json b/tests/inputs/proto3_field_presence/proto3_field_presence.json index 3ed977326..3cc3ccb58 100644 --- a/tests/inputs/proto3_field_presence/proto3_field_presence.json +++ b/tests/inputs/proto3_field_presence/proto3_field_presence.json @@ -6,5 +6,7 @@ "test5": { "test": "Hello" }, - "test6": "B" + "test6": "B", + "test7": "8589934592", + "test8": 2.5 } diff --git a/tests/inputs/proto3_field_presence/proto3_field_presence.proto b/tests/inputs/proto3_field_presence/proto3_field_presence.proto index 1f33cb488..0ccb7005a 100644 --- a/tests/inputs/proto3_field_presence/proto3_field_presence.proto +++ b/tests/inputs/proto3_field_presence/proto3_field_presence.proto @@ -11,6 +11,8 @@ message Test { optional bytes test4 = 4; optional InnerTest test5 = 5; optional TestEnum test6 = 6; + optional uint64 test7 = 7; + optional float test8 = 8; } enum TestEnum { diff --git a/tests/inputs/proto3_field_presence/proto3_field_presence_default.json b/tests/inputs/proto3_field_presence/proto3_field_presence_default.json index 6d0880ead..145d38412 100644 --- a/tests/inputs/proto3_field_presence/proto3_field_presence_default.json +++ b/tests/inputs/proto3_field_presence/proto3_field_presence_default.json @@ -4,5 +4,7 @@ "test3": null, "test4": null, "test5": null, - "test6": null + "test6": null, + "test7": null, + "test8": null } diff --git a/tests/inputs/proto3_field_presence/proto3_field_presence_missing.json b/tests/inputs/proto3_field_presence/proto3_field_presence_missing.json index 66156048d..cc0ed2d9a 100644 --- a/tests/inputs/proto3_field_presence/proto3_field_presence_missing.json +++ b/tests/inputs/proto3_field_presence/proto3_field_presence_missing.json @@ -4,5 +4,7 @@ "test3": "", "test4": "", "test5": null, - "test6": "A" + "test6": "A", + "test7": "0", + "test8": 0 } From be4c164d551474ba5adcd906883c5943f9a213db Mon Sep 17 00:00:00 2001 From: roblabla Date: Thu, 30 Sep 2021 16:34:35 +0200 Subject: [PATCH 08/13] Support field presence for in64 types --- src/betterproto/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/betterproto/__init__.py b/src/betterproto/__init__.py index 50df44434..80b5518de 100644 --- a/src/betterproto/__init__.py +++ b/src/betterproto/__init__.py @@ -1075,6 +1075,8 @@ def to_dict( 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: + output[cased_name] = value else: output[cased_name] = str(value) elif meta.proto_type == TYPE_BYTES: From 04e1133336537cf0b9aacf5958362ce991a5d386 Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Tue, 16 Nov 2021 10:50:15 -0800 Subject: [PATCH 09/13] Fix style --- src/betterproto/__init__.py | 67 ++++++++++++++++++++++++-------- src/betterproto/plugin/parser.py | 1 + 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/betterproto/__init__.py b/src/betterproto/__init__.py index 80b5518de..5e3e43462 100644 --- a/src/betterproto/__init__.py +++ b/src/betterproto/__init__.py @@ -183,66 +183,99 @@ def bool_field(number: int, group: Optional[str] = None, optional: bool = False) return dataclass_field(number, TYPE_BOOL, group=group, optional=optional) -def int32_field(number: int, group: Optional[str] = None, optional: bool = False) -> Any: +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, optional: bool = False) -> Any: +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, optional: bool = False) -> Any: +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, optional: bool = False) -> Any: +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, optional: bool = False) -> Any: +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, optional: bool = False) -> Any: +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, optional: bool = False) -> Any: +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, optional: bool = False) -> Any: +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, optional: bool = False) -> Any: +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, optional: bool = False) -> Any: +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, optional: bool = False) -> Any: +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, optional: bool = False) -> Any: +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, optional: bool = False) -> Any: +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, optional: bool = False) -> Any: +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, optional: bool = False + number: int, + group: Optional[str] = None, + wraps: Optional[str] = None, + optional: bool = False, ) -> Any: - return dataclass_field(number, TYPE_MESSAGE, group=group, wraps=wraps, optional=optional) + return dataclass_field( + number, TYPE_MESSAGE, group=group, wraps=wraps, optional=optional + ) def map_field( @@ -709,7 +742,7 @@ def __bytes__(self) -> bytes: # 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 + # 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 = ( diff --git a/src/betterproto/plugin/parser.py b/src/betterproto/plugin/parser.py index e97275e1b..21a2caf14 100644 --- a/src/betterproto/plugin/parser.py +++ b/src/betterproto/plugin/parser.py @@ -137,6 +137,7 @@ def generate_code(request: CodeGeneratorRequest) -> CodeGeneratorResponse: return response + def read_protobuf_type( item: DescriptorProto, path: List[int], From 4455175f71840c26bc3606379353b147600dea9a Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Tue, 16 Nov 2021 18:33:09 -0800 Subject: [PATCH 10/13] Use python -m to run pytest in CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66e7abab1..1e414ebec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,4 +66,4 @@ jobs: - name: Execute test suite shell: bash - run: poetry run pytest tests/ + run: poetry run python -m pytest tests/ From 6f0944c5087b4146da266e7e4c9dacabaafe220b Mon Sep 17 00:00:00 2001 From: Vincent Thiberville Date: Sun, 12 Dec 2021 03:29:59 +0100 Subject: [PATCH 11/13] 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. --- src/betterproto/__init__.py | 28 ++++++++------ .../proto3_field_presence_default.json | 11 +----- .../proto3_field_presence_missing.json | 1 - .../test_proto3_field_presence.py | 38 +++++++++++++++++++ .../proto3_field_presence_oneof.json | 3 ++ .../proto3_field_presence_oneof.proto | 20 ++++++++++ .../test_proto3_field_presence_oneof.py | 29 ++++++++++++++ 7 files changed, 107 insertions(+), 23 deletions(-) create mode 100644 tests/inputs/proto3_field_presence/test_proto3_field_presence.py create mode 100644 tests/inputs/proto3_field_presence_oneof/proto3_field_presence_oneof.json create mode 100644 tests/inputs/proto3_field_presence_oneof/proto3_field_presence_oneof.proto create mode 100644 tests/inputs/proto3_field_presence_oneof/test_proto3_field_presence_oneof.py diff --git a/src/betterproto/__init__.py b/src/betterproto/__init__.py index 5e3e43462..3cee8887e 100644 --- a/src/betterproto/__init__.py +++ b/src/betterproto/__init__.py @@ -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": @@ -165,7 +167,9 @@ def dataclass_field( return dataclasses.field( 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 + ) }, ) @@ -620,7 +624,8 @@ def __post_init__(self) -> None: 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 @@ -1043,7 +1048,6 @@ def to_dict( defaults = self._betterproto.default_gen for field_name, meta in self._betterproto.meta_by_field_name.items(): field_is_repeated = defaults[field_name] is list - field_is_optional = defaults[field_name] is type(None) value = getattr(self, field_name) cased_name = casing(field_name).rstrip("_") # type: ignore if meta.proto_type == TYPE_MESSAGE: @@ -1082,7 +1086,8 @@ def to_dict( if value or include_default_values: output[cased_name] = value elif value is None: - output[cased_name] = None + if include_default_values: + output[cased_name] = value elif ( value._serialized_on_wire or include_default_values @@ -1109,7 +1114,8 @@ def to_dict( if field_is_repeated: output[cased_name] = [str(n) for n in value] elif value is None: - output[cased_name] = value + if include_default_values: + output[cased_name] = value else: output[cased_name] = str(value) elif meta.proto_type == TYPE_BYTES: @@ -1117,8 +1123,8 @@ def to_dict( output[cased_name] = [ b64encode(b).decode("utf8") for b in value ] - elif value is None: - output[cased_name] = None + 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: @@ -1132,8 +1138,9 @@ def to_dict( # transparently upgrade single value to repeated output[cased_name] = [enum_class(value).name] elif value is None: - output[cased_name] = None - elif field_is_optional: + 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: @@ -1173,9 +1180,6 @@ def from_dict(self: T, value: Dict[str, Any]) -> T: if value[key] is not None: if meta.proto_type == TYPE_MESSAGE: v = getattr(self, field_name) - if value[key] is None and self._get_field_default(key) == None: - # Setting an optional value to None. - setattr(self, field_name, None) if isinstance(v, list): cls = self._betterproto.cls_by_field[field_name] if cls == datetime: diff --git a/tests/inputs/proto3_field_presence/proto3_field_presence_default.json b/tests/inputs/proto3_field_presence/proto3_field_presence_default.json index 145d38412..0967ef424 100644 --- a/tests/inputs/proto3_field_presence/proto3_field_presence_default.json +++ b/tests/inputs/proto3_field_presence/proto3_field_presence_default.json @@ -1,10 +1 @@ -{ - "test1": null, - "test2": null, - "test3": null, - "test4": null, - "test5": null, - "test6": null, - "test7": null, - "test8": null -} +{} diff --git a/tests/inputs/proto3_field_presence/proto3_field_presence_missing.json b/tests/inputs/proto3_field_presence/proto3_field_presence_missing.json index cc0ed2d9a..b19ae9804 100644 --- a/tests/inputs/proto3_field_presence/proto3_field_presence_missing.json +++ b/tests/inputs/proto3_field_presence/proto3_field_presence_missing.json @@ -3,7 +3,6 @@ "test2": false, "test3": "", "test4": "", - "test5": null, "test6": "A", "test7": "0", "test8": 0 diff --git a/tests/inputs/proto3_field_presence/test_proto3_field_presence.py b/tests/inputs/proto3_field_presence/test_proto3_field_presence.py new file mode 100644 index 000000000..364e0cce0 --- /dev/null +++ b/tests/inputs/proto3_field_presence/test_proto3_field_presence.py @@ -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, + } diff --git a/tests/inputs/proto3_field_presence_oneof/proto3_field_presence_oneof.json b/tests/inputs/proto3_field_presence_oneof/proto3_field_presence_oneof.json new file mode 100644 index 000000000..da0819278 --- /dev/null +++ b/tests/inputs/proto3_field_presence_oneof/proto3_field_presence_oneof.json @@ -0,0 +1,3 @@ +{ + "nested": {} +} diff --git a/tests/inputs/proto3_field_presence_oneof/proto3_field_presence_oneof.proto b/tests/inputs/proto3_field_presence_oneof/proto3_field_presence_oneof.proto new file mode 100644 index 000000000..c4dc9d4f8 --- /dev/null +++ b/tests/inputs/proto3_field_presence_oneof/proto3_field_presence_oneof.proto @@ -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; +} diff --git a/tests/inputs/proto3_field_presence_oneof/test_proto3_field_presence_oneof.py b/tests/inputs/proto3_field_presence_oneof/test_proto3_field_presence_oneof.py new file mode 100644 index 000000000..0092a23b5 --- /dev/null +++ b/tests/inputs/proto3_field_presence_oneof/test_proto3_field_presence_oneof.py @@ -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))) From 3a41992adacc1696b3baa4e3f1d9b21f3474faa5 Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Wed, 29 Dec 2021 12:04:06 -0800 Subject: [PATCH 12/13] Fix: use builtin annotation prefix --- src/betterproto/plugin/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/betterproto/plugin/models.py b/src/betterproto/plugin/models.py index c693bf946..732435d0d 100644 --- a/src/betterproto/plugin/models.py +++ b/src/betterproto/plugin/models.py @@ -539,10 +539,10 @@ def annotation(self) -> str: if self.use_builtins: py_type = f"builtins.{py_type}" if self.repeated: - return f"List[{self.py_type}]" + return f"List[{py_type}]" if self.optional: - return f"Optional[{self.py_type}]" - return self.py_type + return f"Optional[{py_type}]" + return py_type @dataclass From f3d65c2318a6488912a032f1cf8585be51e338dc Mon Sep 17 00:00:00 2001 From: kalzoo <22137047+kalzoo@users.noreply.github.com> Date: Wed, 29 Dec 2021 12:05:33 -0800 Subject: [PATCH 13/13] Remove comment --- src/betterproto/plugin/models.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/betterproto/plugin/models.py b/src/betterproto/plugin/models.py index 732435d0d..840140043 100644 --- a/src/betterproto/plugin/models.py +++ b/src/betterproto/plugin/models.py @@ -448,8 +448,6 @@ def repeated(self) -> bool: @property def optional(self) -> bool: - # TODO: Should proto2 optional fields with kind=Message also be - # considered Optional. return self.proto_obj.proto3_optional @property