From 0fa75e71dfc6d56d2c0eaf214a48774b99bb959f Mon Sep 17 00:00:00 2001 From: "Bob \"Wombat\" Hogg" Date: Wed, 27 Sep 2023 12:30:14 -0400 Subject: [PATCH 01/21] docs(__init__): Note that Firestore in Datastore Mode is supported (#919) --- google/cloud/ndb/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/cloud/ndb/__init__.py b/google/cloud/ndb/__init__.py index c7475006..fa82bf91 100644 --- a/google/cloud/ndb/__init__.py +++ b/google/cloud/ndb/__init__.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""``ndb`` is a library for Google Cloud Datastore. +"""``ndb`` is a library for Google Cloud Firestore in Datastore Mode and Google Cloud Datastore. It was originally included in the Google App Engine runtime as a "new" version of the ``db`` API (hence ``ndb``). From 45c4b1f97171c57040354f45e812c7e3e7f2ba36 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 13:37:03 -0400 Subject: [PATCH 02/21] chore: [autoapprove] bump cryptography from 41.0.3 to 41.0.4 (#921) Source-Link: https://github.com/googleapis/synthtool/commit/dede53ff326079b457cfb1aae5bbdc82cbb51dc3 Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:fac304457974bb530cc5396abd4ab25d26a469cd3bc97cbfb18c8d4324c584eb Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 ++-- .kokoro/requirements.txt | 49 ++++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index a3da1b0d..a9bdb1b7 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,5 +13,5 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:3e3800bb100af5d7f9e810d48212b37812c1856d20ffeafb99ebe66461b61fc7 -# created: 2023-08-02T10:53:29.114535628Z + digest: sha256:fac304457974bb530cc5396abd4ab25d26a469cd3bc97cbfb18c8d4324c584eb +# created: 2023-10-02T21:31:03.517640371Z diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 029bd342..96d593c8 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -113,30 +113,30 @@ commonmark==0.9.1 \ --hash=sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60 \ --hash=sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9 # via rich -cryptography==41.0.3 \ - --hash=sha256:0d09fb5356f975974dbcb595ad2d178305e5050656affb7890a1583f5e02a306 \ - --hash=sha256:23c2d778cf829f7d0ae180600b17e9fceea3c2ef8b31a99e3c694cbbf3a24b84 \ - --hash=sha256:3fb248989b6363906827284cd20cca63bb1a757e0a2864d4c1682a985e3dca47 \ - --hash=sha256:41d7aa7cdfded09b3d73a47f429c298e80796c8e825ddfadc84c8a7f12df212d \ - --hash=sha256:42cb413e01a5d36da9929baa9d70ca90d90b969269e5a12d39c1e0d475010116 \ - --hash=sha256:4c2f0d35703d61002a2bbdcf15548ebb701cfdd83cdc12471d2bae80878a4207 \ - --hash=sha256:4fd871184321100fb400d759ad0cddddf284c4b696568204d281c902fc7b0d81 \ - --hash=sha256:5259cb659aa43005eb55a0e4ff2c825ca111a0da1814202c64d28a985d33b087 \ - --hash=sha256:57a51b89f954f216a81c9d057bf1a24e2f36e764a1ca9a501a6964eb4a6800dd \ - --hash=sha256:652627a055cb52a84f8c448185922241dd5217443ca194d5739b44612c5e6507 \ - --hash=sha256:67e120e9a577c64fe1f611e53b30b3e69744e5910ff3b6e97e935aeb96005858 \ - --hash=sha256:6af1c6387c531cd364b72c28daa29232162010d952ceb7e5ca8e2827526aceae \ - --hash=sha256:6d192741113ef5e30d89dcb5b956ef4e1578f304708701b8b73d38e3e1461f34 \ - --hash=sha256:7efe8041897fe7a50863e51b77789b657a133c75c3b094e51b5e4b5cec7bf906 \ - --hash=sha256:84537453d57f55a50a5b6835622ee405816999a7113267739a1b4581f83535bd \ - --hash=sha256:8f09daa483aedea50d249ef98ed500569841d6498aa9c9f4b0531b9964658922 \ - --hash=sha256:95dd7f261bb76948b52a5330ba5202b91a26fbac13ad0e9fc8a3ac04752058c7 \ - --hash=sha256:a74fbcdb2a0d46fe00504f571a2a540532f4c188e6ccf26f1f178480117b33c4 \ - --hash=sha256:a983e441a00a9d57a4d7c91b3116a37ae602907a7618b882c8013b5762e80574 \ - --hash=sha256:ab8de0d091acbf778f74286f4989cf3d1528336af1b59f3e5d2ebca8b5fe49e1 \ - --hash=sha256:aeb57c421b34af8f9fe830e1955bf493a86a7996cc1338fe41b30047d16e962c \ - --hash=sha256:ce785cf81a7bdade534297ef9e490ddff800d956625020ab2ec2780a556c313e \ - --hash=sha256:d0d651aa754ef58d75cec6edfbd21259d93810b73f6ec246436a21b7841908de +cryptography==41.0.4 \ + --hash=sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67 \ + --hash=sha256:047c4603aeb4bbd8db2756e38f5b8bd7e94318c047cfe4efeb5d715e08b49311 \ + --hash=sha256:0d9409894f495d465fe6fda92cb70e8323e9648af912d5b9141d616df40a87b8 \ + --hash=sha256:23a25c09dfd0d9f28da2352503b23e086f8e78096b9fd585d1d14eca01613e13 \ + --hash=sha256:2ed09183922d66c4ec5fdaa59b4d14e105c084dd0febd27452de8f6f74704143 \ + --hash=sha256:35c00f637cd0b9d5b6c6bd11b6c3359194a8eba9c46d4e875a3660e3b400005f \ + --hash=sha256:37480760ae08065437e6573d14be973112c9e6dcaf5f11d00147ee74f37a3829 \ + --hash=sha256:3b224890962a2d7b57cf5eeb16ccaafba6083f7b811829f00476309bce2fe0fd \ + --hash=sha256:5a0f09cefded00e648a127048119f77bc2b2ec61e736660b5789e638f43cc397 \ + --hash=sha256:5b72205a360f3b6176485a333256b9bcd48700fc755fef51c8e7e67c4b63e3ac \ + --hash=sha256:7e53db173370dea832190870e975a1e09c86a879b613948f09eb49324218c14d \ + --hash=sha256:7febc3094125fc126a7f6fb1f420d0da639f3f32cb15c8ff0dc3997c4549f51a \ + --hash=sha256:80907d3faa55dc5434a16579952ac6da800935cd98d14dbd62f6f042c7f5e839 \ + --hash=sha256:86defa8d248c3fa029da68ce61fe735432b047e32179883bdb1e79ed9bb8195e \ + --hash=sha256:8ac4f9ead4bbd0bc8ab2d318f97d85147167a488be0e08814a37eb2f439d5cf6 \ + --hash=sha256:93530900d14c37a46ce3d6c9e6fd35dbe5f5601bf6b3a5c325c7bffc030344d9 \ + --hash=sha256:9eeb77214afae972a00dee47382d2591abe77bdae166bda672fb1e24702a3860 \ + --hash=sha256:b5f4dfe950ff0479f1f00eda09c18798d4f49b98f4e2006d644b3301682ebdca \ + --hash=sha256:c3391bd8e6de35f6f1140e50aaeb3e2b3d6a9012536ca23ab0d9c35ec18c8a91 \ + --hash=sha256:c880eba5175f4307129784eca96f4e70b88e57aa3f680aeba3bab0e980b0f37d \ + --hash=sha256:cecfefa17042941f94ab54f769c8ce0fe14beff2694e9ac684176a2535bf9714 \ + --hash=sha256:e40211b4923ba5a6dc9769eab704bdb3fbb58d56c5b336d30996c24fcf12aadb \ + --hash=sha256:efc8ad4e6fc4f1752ebfb58aefece8b4e3c4cae940b0994d43649bdfce8d0d4f # via # gcp-releasetool # secretstorage @@ -382,6 +382,7 @@ protobuf==3.20.3 \ # gcp-docuploader # gcp-releasetool # google-api-core + # googleapis-common-protos pyasn1==0.4.8 \ --hash=sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d \ --hash=sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba From 8a2a401c682cc2002ba903adfd5a52a80842a618 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 10 Oct 2023 10:09:42 -0400 Subject: [PATCH 03/21] chore: [autoapprove] Update `black` and `isort` to latest versions (#924) Source-Link: https://github.com/googleapis/synthtool/commit/0c7b0333f44b2b7075447f43a121a12d15a7b76a Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:08e34975760f002746b1d8c86fdc90660be45945ee6d9db914d1508acdf9a547 Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 ++-- .kokoro/requirements.txt | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index a9bdb1b7..dd98abbd 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,5 +13,5 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:fac304457974bb530cc5396abd4ab25d26a469cd3bc97cbfb18c8d4324c584eb -# created: 2023-10-02T21:31:03.517640371Z + digest: sha256:08e34975760f002746b1d8c86fdc90660be45945ee6d9db914d1508acdf9a547 +# created: 2023-10-09T14:06:13.397766266Z diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 96d593c8..0332d326 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -467,9 +467,9 @@ typing-extensions==4.4.0 \ --hash=sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa \ --hash=sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e # via -r requirements.in -urllib3==1.26.12 \ - --hash=sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e \ - --hash=sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997 +urllib3==1.26.17 \ + --hash=sha256:24d6a242c28d29af46c3fae832c36db3bbebcc533dd1bb549172cd739c82df21 \ + --hash=sha256:94a757d178c9be92ef5539b8840d48dc9cf1b2709c9d6b588232a055c524458b # via # requests # twine From 58fe7e7fc1317d6d6f3ad2ddd57883cfec4232ac Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Thu, 26 Oct 2023 14:07:25 -0700 Subject: [PATCH 04/21] chore: rename rst files to avoid conflict with service names (#926) Source-Link: https://github.com/googleapis/synthtool/commit/d52e638b37b091054c869bfa6f5a9fedaba9e0dd Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:4f9b3b106ad0beafc2c8a415e3f62c1a0cc23cabea115dbe841b848f581cfe99 Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 ++-- .kokoro/requirements.txt | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index dd98abbd..7f291dbd 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,5 +13,5 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:08e34975760f002746b1d8c86fdc90660be45945ee6d9db914d1508acdf9a547 -# created: 2023-10-09T14:06:13.397766266Z + digest: sha256:4f9b3b106ad0beafc2c8a415e3f62c1a0cc23cabea115dbe841b848f581cfe99 +# created: 2023-10-18T20:26:37.410353675Z diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 0332d326..16170d0c 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -467,9 +467,9 @@ typing-extensions==4.4.0 \ --hash=sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa \ --hash=sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e # via -r requirements.in -urllib3==1.26.17 \ - --hash=sha256:24d6a242c28d29af46c3fae832c36db3bbebcc533dd1bb549172cd739c82df21 \ - --hash=sha256:94a757d178c9be92ef5539b8840d48dc9cf1b2709c9d6b588232a055c524458b +urllib3==1.26.18 \ + --hash=sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07 \ + --hash=sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0 # via # requests # twine From 383a500493bb178443c722f3ee8a636c6003f318 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Wed, 15 Nov 2023 02:32:55 -0500 Subject: [PATCH 05/21] chore: bump urllib3 from 1.26.12 to 1.26.18 (#928) Source-Link: https://github.com/googleapis/synthtool/commit/febacccc98d6d224aff9d0bd0373bb5a4cd5969c Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:caffe0a9277daeccc4d1de5c9b55ebba0901b57c2f713ec9c876b0d4ec064f61 Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 +- .kokoro/requirements.txt | 532 ++++++++++++++++++++------------------ 2 files changed, 277 insertions(+), 259 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 7f291dbd..453b540c 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,5 +13,5 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:4f9b3b106ad0beafc2c8a415e3f62c1a0cc23cabea115dbe841b848f581cfe99 -# created: 2023-10-18T20:26:37.410353675Z + digest: sha256:caffe0a9277daeccc4d1de5c9b55ebba0901b57c2f713ec9c876b0d4ec064f61 +# created: 2023-11-08T19:46:45.022803742Z diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 16170d0c..8957e211 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -4,91 +4,75 @@ # # pip-compile --allow-unsafe --generate-hashes requirements.in # -argcomplete==2.0.0 \ - --hash=sha256:6372ad78c89d662035101418ae253668445b391755cfe94ea52f1b9d22425b20 \ - --hash=sha256:cffa11ea77999bb0dd27bb25ff6dc142a6796142f68d45b1a26b11f58724561e +argcomplete==3.1.4 \ + --hash=sha256:72558ba729e4c468572609817226fb0a6e7e9a0a7d477b882be168c0b4a62b94 \ + --hash=sha256:fbe56f8cda08aa9a04b307d8482ea703e96a6a801611acb4be9bf3942017989f # via nox -attrs==22.1.0 \ - --hash=sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6 \ - --hash=sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c +attrs==23.1.0 \ + --hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \ + --hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015 # via gcp-releasetool -bleach==5.0.1 \ - --hash=sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a \ - --hash=sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c - # via readme-renderer -cachetools==5.2.0 \ - --hash=sha256:6a94c6402995a99c3970cc7e4884bb60b4a8639938157eeed436098bf9831757 \ - --hash=sha256:f9f17d2aec496a9aa6b76f53e3b614c965223c061982d434d160f930c698a9db +cachetools==5.3.2 \ + --hash=sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2 \ + --hash=sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1 # via google-auth certifi==2023.7.22 \ --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \ --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9 # via requests -cffi==1.15.1 \ - --hash=sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5 \ - --hash=sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef \ - --hash=sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104 \ - --hash=sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426 \ - --hash=sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405 \ - --hash=sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375 \ - --hash=sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a \ - --hash=sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e \ - --hash=sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc \ - --hash=sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf \ - --hash=sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185 \ - --hash=sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497 \ - --hash=sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3 \ - --hash=sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35 \ - --hash=sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c \ - --hash=sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83 \ - --hash=sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21 \ - --hash=sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca \ - --hash=sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984 \ - --hash=sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac \ - --hash=sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd \ - --hash=sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee \ - --hash=sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a \ - --hash=sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2 \ - --hash=sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192 \ - --hash=sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7 \ - --hash=sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585 \ - --hash=sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f \ - --hash=sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e \ - --hash=sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27 \ - --hash=sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b \ - --hash=sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e \ - --hash=sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e \ - --hash=sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d \ - --hash=sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c \ - --hash=sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415 \ - --hash=sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82 \ - --hash=sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02 \ - --hash=sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314 \ - --hash=sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325 \ - --hash=sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c \ - --hash=sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3 \ - --hash=sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914 \ - --hash=sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045 \ - --hash=sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d \ - --hash=sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9 \ - --hash=sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5 \ - --hash=sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2 \ - --hash=sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c \ - --hash=sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3 \ - --hash=sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2 \ - --hash=sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8 \ - --hash=sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d \ - --hash=sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d \ - --hash=sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9 \ - --hash=sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162 \ - --hash=sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76 \ - --hash=sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4 \ - --hash=sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e \ - --hash=sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9 \ - --hash=sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6 \ - --hash=sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b \ - --hash=sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01 \ - --hash=sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0 +cffi==1.16.0 \ + --hash=sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc \ + --hash=sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a \ + --hash=sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417 \ + --hash=sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab \ + --hash=sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520 \ + --hash=sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36 \ + --hash=sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743 \ + --hash=sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8 \ + --hash=sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed \ + --hash=sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684 \ + --hash=sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56 \ + --hash=sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324 \ + --hash=sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d \ + --hash=sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235 \ + --hash=sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e \ + --hash=sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088 \ + --hash=sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000 \ + --hash=sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7 \ + --hash=sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e \ + --hash=sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673 \ + --hash=sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c \ + --hash=sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe \ + --hash=sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2 \ + --hash=sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098 \ + --hash=sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8 \ + --hash=sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a \ + --hash=sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0 \ + --hash=sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b \ + --hash=sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896 \ + --hash=sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e \ + --hash=sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9 \ + --hash=sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2 \ + --hash=sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b \ + --hash=sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6 \ + --hash=sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404 \ + --hash=sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f \ + --hash=sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0 \ + --hash=sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4 \ + --hash=sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc \ + --hash=sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936 \ + --hash=sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba \ + --hash=sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872 \ + --hash=sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb \ + --hash=sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614 \ + --hash=sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1 \ + --hash=sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d \ + --hash=sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969 \ + --hash=sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b \ + --hash=sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4 \ + --hash=sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627 \ + --hash=sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956 \ + --hash=sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357 # via cryptography charset-normalizer==2.1.1 \ --hash=sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845 \ @@ -109,78 +93,74 @@ colorlog==6.7.0 \ # via # gcp-docuploader # nox -commonmark==0.9.1 \ - --hash=sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60 \ - --hash=sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9 - # via rich -cryptography==41.0.4 \ - --hash=sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67 \ - --hash=sha256:047c4603aeb4bbd8db2756e38f5b8bd7e94318c047cfe4efeb5d715e08b49311 \ - --hash=sha256:0d9409894f495d465fe6fda92cb70e8323e9648af912d5b9141d616df40a87b8 \ - --hash=sha256:23a25c09dfd0d9f28da2352503b23e086f8e78096b9fd585d1d14eca01613e13 \ - --hash=sha256:2ed09183922d66c4ec5fdaa59b4d14e105c084dd0febd27452de8f6f74704143 \ - --hash=sha256:35c00f637cd0b9d5b6c6bd11b6c3359194a8eba9c46d4e875a3660e3b400005f \ - --hash=sha256:37480760ae08065437e6573d14be973112c9e6dcaf5f11d00147ee74f37a3829 \ - --hash=sha256:3b224890962a2d7b57cf5eeb16ccaafba6083f7b811829f00476309bce2fe0fd \ - --hash=sha256:5a0f09cefded00e648a127048119f77bc2b2ec61e736660b5789e638f43cc397 \ - --hash=sha256:5b72205a360f3b6176485a333256b9bcd48700fc755fef51c8e7e67c4b63e3ac \ - --hash=sha256:7e53db173370dea832190870e975a1e09c86a879b613948f09eb49324218c14d \ - --hash=sha256:7febc3094125fc126a7f6fb1f420d0da639f3f32cb15c8ff0dc3997c4549f51a \ - --hash=sha256:80907d3faa55dc5434a16579952ac6da800935cd98d14dbd62f6f042c7f5e839 \ - --hash=sha256:86defa8d248c3fa029da68ce61fe735432b047e32179883bdb1e79ed9bb8195e \ - --hash=sha256:8ac4f9ead4bbd0bc8ab2d318f97d85147167a488be0e08814a37eb2f439d5cf6 \ - --hash=sha256:93530900d14c37a46ce3d6c9e6fd35dbe5f5601bf6b3a5c325c7bffc030344d9 \ - --hash=sha256:9eeb77214afae972a00dee47382d2591abe77bdae166bda672fb1e24702a3860 \ - --hash=sha256:b5f4dfe950ff0479f1f00eda09c18798d4f49b98f4e2006d644b3301682ebdca \ - --hash=sha256:c3391bd8e6de35f6f1140e50aaeb3e2b3d6a9012536ca23ab0d9c35ec18c8a91 \ - --hash=sha256:c880eba5175f4307129784eca96f4e70b88e57aa3f680aeba3bab0e980b0f37d \ - --hash=sha256:cecfefa17042941f94ab54f769c8ce0fe14beff2694e9ac684176a2535bf9714 \ - --hash=sha256:e40211b4923ba5a6dc9769eab704bdb3fbb58d56c5b336d30996c24fcf12aadb \ - --hash=sha256:efc8ad4e6fc4f1752ebfb58aefece8b4e3c4cae940b0994d43649bdfce8d0d4f +cryptography==41.0.5 \ + --hash=sha256:0c327cac00f082013c7c9fb6c46b7cc9fa3c288ca702c74773968173bda421bf \ + --hash=sha256:0d2a6a598847c46e3e321a7aef8af1436f11c27f1254933746304ff014664d84 \ + --hash=sha256:227ec057cd32a41c6651701abc0328135e472ed450f47c2766f23267b792a88e \ + --hash=sha256:22892cc830d8b2c89ea60148227631bb96a7da0c1b722f2aac8824b1b7c0b6b8 \ + --hash=sha256:392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7 \ + --hash=sha256:3be3ca726e1572517d2bef99a818378bbcf7d7799d5372a46c79c29eb8d166c1 \ + --hash=sha256:573eb7128cbca75f9157dcde974781209463ce56b5804983e11a1c462f0f4e88 \ + --hash=sha256:580afc7b7216deeb87a098ef0674d6ee34ab55993140838b14c9b83312b37b86 \ + --hash=sha256:5a70187954ba7292c7876734183e810b728b4f3965fbe571421cb2434d279179 \ + --hash=sha256:73801ac9736741f220e20435f84ecec75ed70eda90f781a148f1bad546963d81 \ + --hash=sha256:7d208c21e47940369accfc9e85f0de7693d9a5d843c2509b3846b2db170dfd20 \ + --hash=sha256:8254962e6ba1f4d2090c44daf50a547cd5f0bf446dc658a8e5f8156cae0d8548 \ + --hash=sha256:88417bff20162f635f24f849ab182b092697922088b477a7abd6664ddd82291d \ + --hash=sha256:a48e74dad1fb349f3dc1d449ed88e0017d792997a7ad2ec9587ed17405667e6d \ + --hash=sha256:b948e09fe5fb18517d99994184854ebd50b57248736fd4c720ad540560174ec5 \ + --hash=sha256:c707f7afd813478e2019ae32a7c49cd932dd60ab2d2a93e796f68236b7e1fbf1 \ + --hash=sha256:d38e6031e113b7421db1de0c1b1f7739564a88f1684c6b89234fbf6c11b75147 \ + --hash=sha256:d3977f0e276f6f5bf245c403156673db103283266601405376f075c849a0b936 \ + --hash=sha256:da6a0ff8f1016ccc7477e6339e1d50ce5f59b88905585f77193ebd5068f1e797 \ + --hash=sha256:e270c04f4d9b5671ebcc792b3ba5d4488bf7c42c3c241a3748e2599776f29696 \ + --hash=sha256:e886098619d3815e0ad5790c973afeee2c0e6e04b4da90b88e6bd06e2a0b1b72 \ + --hash=sha256:ec3b055ff8f1dce8e6ef28f626e0972981475173d7973d63f271b29c8a2897da \ + --hash=sha256:fba1e91467c65fe64a82c689dc6cf58151158993b13eb7a7f3f4b7f395636723 # via # gcp-releasetool # secretstorage -distlib==0.3.6 \ - --hash=sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46 \ - --hash=sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e +distlib==0.3.7 \ + --hash=sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057 \ + --hash=sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8 # via virtualenv -docutils==0.19 \ - --hash=sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6 \ - --hash=sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc +docutils==0.20.1 \ + --hash=sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6 \ + --hash=sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b # via readme-renderer -filelock==3.8.0 \ - --hash=sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc \ - --hash=sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4 +filelock==3.13.1 \ + --hash=sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e \ + --hash=sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c # via virtualenv -gcp-docuploader==0.6.4 \ - --hash=sha256:01486419e24633af78fd0167db74a2763974765ee8078ca6eb6964d0ebd388af \ - --hash=sha256:70861190c123d907b3b067da896265ead2eeb9263969d6955c9e0bb091b5ccbf +gcp-docuploader==0.6.5 \ + --hash=sha256:30221d4ac3e5a2b9c69aa52fdbef68cc3f27d0e6d0d90e220fc024584b8d2318 \ + --hash=sha256:b7458ef93f605b9d46a4bf3a8dc1755dad1f31d030c8679edf304e343b347eea # via -r requirements.in -gcp-releasetool==1.10.5 \ - --hash=sha256:174b7b102d704b254f2a26a3eda2c684fd3543320ec239baf771542a2e58e109 \ - --hash=sha256:e29d29927fe2ca493105a82958c6873bb2b90d503acac56be2c229e74de0eec9 +gcp-releasetool==1.16.0 \ + --hash=sha256:27bf19d2e87aaa884096ff941aa3c592c482be3d6a2bfe6f06afafa6af2353e3 \ + --hash=sha256:a316b197a543fd036209d0caba7a8eb4d236d8e65381c80cbc6d7efaa7606d63 # via -r requirements.in -google-api-core==2.10.2 \ - --hash=sha256:10c06f7739fe57781f87523375e8e1a3a4674bf6392cd6131a3222182b971320 \ - --hash=sha256:34f24bd1d5f72a8c4519773d99ca6bf080a6c4e041b4e9f024fe230191dda62e +google-api-core==2.12.0 \ + --hash=sha256:c22e01b1e3c4dcd90998494879612c38d0a3411d1f7b679eb89e2abe3ce1f553 \ + --hash=sha256:ec6054f7d64ad13b41e43d96f735acbd763b0f3b695dabaa2d579673f6a6e160 # via # google-cloud-core # google-cloud-storage -google-auth==2.14.1 \ - --hash=sha256:ccaa901f31ad5cbb562615eb8b664b3dd0bf5404a67618e642307f00613eda4d \ - --hash=sha256:f5d8701633bebc12e0deea4df8abd8aff31c28b355360597f7f2ee60f2e4d016 +google-auth==2.23.4 \ + --hash=sha256:79905d6b1652187def79d491d6e23d0cbb3a21d3c7ba0dbaa9c8a01906b13ff3 \ + --hash=sha256:d4bbc92fe4b8bfd2f3e8d88e5ba7085935da208ee38a134fc280e7ce682a05f2 # via # gcp-releasetool # google-api-core # google-cloud-core # google-cloud-storage -google-cloud-core==2.3.2 \ - --hash=sha256:8417acf6466be2fa85123441696c4badda48db314c607cf1e5d543fa8bdc22fe \ - --hash=sha256:b9529ee7047fd8d4bf4a2182de619154240df17fbe60ead399078c1ae152af9a +google-cloud-core==2.3.3 \ + --hash=sha256:37b80273c8d7eee1ae816b3a20ae43585ea50506cb0e60f3cf5be5f87f1373cb \ + --hash=sha256:fbd11cad3e98a7e5b0343dc07cb1039a5ffd7a5bb96e1f1e27cee4bda4a90863 # via google-cloud-storage -google-cloud-storage==2.6.0 \ - --hash=sha256:104ca28ae61243b637f2f01455cc8a05e8f15a2a18ced96cb587241cdd3820f5 \ - --hash=sha256:4ad0415ff61abdd8bb2ae81c1f8f7ec7d91a1011613f2db87c614c550f97bfe9 +google-cloud-storage==2.13.0 \ + --hash=sha256:ab0bf2e1780a1b74cf17fccb13788070b729f50c252f0c94ada2aae0ca95437d \ + --hash=sha256:f62dc4c7b6cd4360d072e3deb28035fbdad491ac3d9b0b1815a12daea10f37c7 # via gcp-docuploader google-crc32c==1.5.0 \ --hash=sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a \ @@ -251,29 +231,31 @@ google-crc32c==1.5.0 \ --hash=sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183 \ --hash=sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556 \ --hash=sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4 - # via google-resumable-media -google-resumable-media==2.4.0 \ - --hash=sha256:2aa004c16d295c8f6c33b2b4788ba59d366677c0a25ae7382436cb30f776deaa \ - --hash=sha256:8d5518502f92b9ecc84ac46779bd4f09694ecb3ba38a3e7ca737a86d15cbca1f + # via + # google-cloud-storage + # google-resumable-media +google-resumable-media==2.6.0 \ + --hash=sha256:972852f6c65f933e15a4a210c2b96930763b47197cdf4aa5f5bea435efb626e7 \ + --hash=sha256:fc03d344381970f79eebb632a3c18bb1828593a2dc5572b5f90115ef7d11e81b # via google-cloud-storage -googleapis-common-protos==1.57.0 \ - --hash=sha256:27a849d6205838fb6cc3c1c21cb9800707a661bb21c6ce7fb13e99eb1f8a0c46 \ - --hash=sha256:a9f4a1d7f6d9809657b7f1316a1aa527f6664891531bcfcc13b6696e685f443c +googleapis-common-protos==1.61.0 \ + --hash=sha256:22f1915393bb3245343f6efe87f6fe868532efc12aa26b391b15132e1279f1c0 \ + --hash=sha256:8a64866a97f6304a7179873a465d6eee97b7a24ec6cfd78e0f575e96b821240b # via google-api-core idna==3.4 \ --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 # via requests -importlib-metadata==5.0.0 \ - --hash=sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab \ - --hash=sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43 +importlib-metadata==6.8.0 \ + --hash=sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb \ + --hash=sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743 # via # -r requirements.in # keyring # twine -jaraco-classes==3.2.3 \ - --hash=sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158 \ - --hash=sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a +jaraco-classes==3.3.0 \ + --hash=sha256:10afa92b6743f25c0cf5f37c6bb6e18e2c5bb84a16527ccfc0040ea377e7aaeb \ + --hash=sha256:c063dd08e89217cee02c8d5e5ec560f2c8ce6cdc2fcdc2e68f7b2e5547ed3621 # via keyring jeepney==0.8.0 \ --hash=sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806 \ @@ -285,75 +267,121 @@ jinja2==3.1.2 \ --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \ --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61 # via gcp-releasetool -keyring==23.11.0 \ - --hash=sha256:3dd30011d555f1345dec2c262f0153f2f0ca6bca041fb1dc4588349bb4c0ac1e \ - --hash=sha256:ad192263e2cdd5f12875dedc2da13534359a7e760e77f8d04b50968a821c2361 +keyring==24.2.0 \ + --hash=sha256:4901caaf597bfd3bbd78c9a0c7c4c29fcd8310dab2cffefe749e916b6527acd6 \ + --hash=sha256:ca0746a19ec421219f4d713f848fa297a661a8a8c1504867e55bfb5e09091509 # via # gcp-releasetool # twine -markupsafe==2.1.1 \ - --hash=sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003 \ - --hash=sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88 \ - --hash=sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5 \ - --hash=sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7 \ - --hash=sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a \ - --hash=sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603 \ - --hash=sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1 \ - --hash=sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135 \ - --hash=sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247 \ - --hash=sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6 \ - --hash=sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601 \ - --hash=sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77 \ - --hash=sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02 \ - --hash=sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e \ - --hash=sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63 \ - --hash=sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f \ - --hash=sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980 \ - --hash=sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b \ - --hash=sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812 \ - --hash=sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff \ - --hash=sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96 \ - --hash=sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1 \ - --hash=sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925 \ - --hash=sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a \ - --hash=sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6 \ - --hash=sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e \ - --hash=sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f \ - --hash=sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4 \ - --hash=sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f \ - --hash=sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3 \ - --hash=sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c \ - --hash=sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a \ - --hash=sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417 \ - --hash=sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a \ - --hash=sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a \ - --hash=sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37 \ - --hash=sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452 \ - --hash=sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933 \ - --hash=sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a \ - --hash=sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7 +markdown-it-py==3.0.0 \ + --hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \ + --hash=sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb + # via rich +markupsafe==2.1.3 \ + --hash=sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e \ + --hash=sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e \ + --hash=sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431 \ + --hash=sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686 \ + --hash=sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c \ + --hash=sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559 \ + --hash=sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc \ + --hash=sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb \ + --hash=sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939 \ + --hash=sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c \ + --hash=sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0 \ + --hash=sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4 \ + --hash=sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9 \ + --hash=sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575 \ + --hash=sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba \ + --hash=sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d \ + --hash=sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd \ + --hash=sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3 \ + --hash=sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00 \ + --hash=sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155 \ + --hash=sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac \ + --hash=sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52 \ + --hash=sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f \ + --hash=sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8 \ + --hash=sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b \ + --hash=sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007 \ + --hash=sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24 \ + --hash=sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea \ + --hash=sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198 \ + --hash=sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0 \ + --hash=sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee \ + --hash=sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be \ + --hash=sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2 \ + --hash=sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1 \ + --hash=sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707 \ + --hash=sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6 \ + --hash=sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c \ + --hash=sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58 \ + --hash=sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823 \ + --hash=sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779 \ + --hash=sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636 \ + --hash=sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c \ + --hash=sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad \ + --hash=sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee \ + --hash=sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc \ + --hash=sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2 \ + --hash=sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48 \ + --hash=sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7 \ + --hash=sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e \ + --hash=sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b \ + --hash=sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa \ + --hash=sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5 \ + --hash=sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e \ + --hash=sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb \ + --hash=sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9 \ + --hash=sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57 \ + --hash=sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc \ + --hash=sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc \ + --hash=sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2 \ + --hash=sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11 # via jinja2 -more-itertools==9.0.0 \ - --hash=sha256:250e83d7e81d0c87ca6bd942e6aeab8cc9daa6096d12c5308f3f92fa5e5c1f41 \ - --hash=sha256:5a6257e40878ef0520b1803990e3e22303a41b5714006c32a3fd8304b26ea1ab +mdurl==0.1.2 \ + --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ + --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba + # via markdown-it-py +more-itertools==10.1.0 \ + --hash=sha256:626c369fa0eb37bac0291bce8259b332fd59ac792fa5497b59837309cd5b114a \ + --hash=sha256:64e0735fcfdc6f3464ea133afe8ea4483b1c5fe3a3d69852e6503b43a0b222e6 # via jaraco-classes -nox==2022.11.21 \ - --hash=sha256:0e41a990e290e274cb205a976c4c97ee3c5234441a8132c8c3fd9ea3c22149eb \ - --hash=sha256:e21c31de0711d1274ca585a2c5fde36b1aa962005ba8e9322bf5eeed16dcd684 +nh3==0.2.14 \ + --hash=sha256:116c9515937f94f0057ef50ebcbcc10600860065953ba56f14473ff706371873 \ + --hash=sha256:18415df36db9b001f71a42a3a5395db79cf23d556996090d293764436e98e8ad \ + --hash=sha256:203cac86e313cf6486704d0ec620a992c8bc164c86d3a4fd3d761dd552d839b5 \ + --hash=sha256:2b0be5c792bd43d0abef8ca39dd8acb3c0611052ce466d0401d51ea0d9aa7525 \ + --hash=sha256:377aaf6a9e7c63962f367158d808c6a1344e2b4f83d071c43fbd631b75c4f0b2 \ + --hash=sha256:525846c56c2bcd376f5eaee76063ebf33cf1e620c1498b2a40107f60cfc6054e \ + --hash=sha256:5529a3bf99402c34056576d80ae5547123f1078da76aa99e8ed79e44fa67282d \ + --hash=sha256:7771d43222b639a4cd9e341f870cee336b9d886de1ad9bec8dddab22fe1de450 \ + --hash=sha256:88c753efbcdfc2644a5012938c6b9753f1c64a5723a67f0301ca43e7b85dcf0e \ + --hash=sha256:93a943cfd3e33bd03f77b97baa11990148687877b74193bf777956b67054dcc6 \ + --hash=sha256:9be2f68fb9a40d8440cbf34cbf40758aa7f6093160bfc7fb018cce8e424f0c3a \ + --hash=sha256:a0c509894fd4dccdff557068e5074999ae3b75f4c5a2d6fb5415e782e25679c4 \ + --hash=sha256:ac8056e937f264995a82bf0053ca898a1cb1c9efc7cd68fa07fe0060734df7e4 \ + --hash=sha256:aed56a86daa43966dd790ba86d4b810b219f75b4bb737461b6886ce2bde38fd6 \ + --hash=sha256:e8986f1dd3221d1e741fda0a12eaa4a273f1d80a35e31a1ffe579e7c621d069e \ + --hash=sha256:f99212a81c62b5f22f9e7c3e347aa00491114a5647e1f13bbebd79c3e5f08d75 + # via readme-renderer +nox==2023.4.22 \ + --hash=sha256:0b1adc619c58ab4fa57d6ab2e7823fe47a32e70202f287d78474adcc7bda1891 \ + --hash=sha256:46c0560b0dc609d7d967dc99e22cb463d3c4caf54a5fda735d6c11b5177e3a9f # via -r requirements.in -packaging==21.3 \ - --hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \ - --hash=sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522 +packaging==23.2 \ + --hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \ + --hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7 # via # gcp-releasetool # nox -pkginfo==1.8.3 \ - --hash=sha256:848865108ec99d4901b2f7e84058b6e7660aae8ae10164e015a6dcf5b242a594 \ - --hash=sha256:a84da4318dd86f870a9447a8c98340aa06216bfc6f2b7bdc4b8766984ae1867c +pkginfo==1.9.6 \ + --hash=sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546 \ + --hash=sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046 # via twine -platformdirs==2.5.4 \ - --hash=sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7 \ - --hash=sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10 +platformdirs==3.11.0 \ + --hash=sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3 \ + --hash=sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e # via virtualenv protobuf==3.20.3 \ --hash=sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7 \ @@ -383,34 +411,30 @@ protobuf==3.20.3 \ # gcp-releasetool # google-api-core # googleapis-common-protos -pyasn1==0.4.8 \ - --hash=sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d \ - --hash=sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba +pyasn1==0.5.0 \ + --hash=sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57 \ + --hash=sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde # via # pyasn1-modules # rsa -pyasn1-modules==0.2.8 \ - --hash=sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e \ - --hash=sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74 +pyasn1-modules==0.3.0 \ + --hash=sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c \ + --hash=sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d # via google-auth pycparser==2.21 \ --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \ --hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206 # via cffi -pygments==2.15.0 \ - --hash=sha256:77a3299119af881904cd5ecd1ac6a66214b6e9bed1f2db16993b54adede64094 \ - --hash=sha256:f7e36cffc4c517fbc252861b9a6e4644ca0e5abadf9a113c72d1358ad09b9500 +pygments==2.16.1 \ + --hash=sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692 \ + --hash=sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29 # via # readme-renderer # rich -pyjwt==2.6.0 \ - --hash=sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd \ - --hash=sha256:d83c3d892a77bbb74d3e1a2cfa90afaadb60945205d1095d9221f04466f64c14 +pyjwt==2.8.0 \ + --hash=sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de \ + --hash=sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320 # via gcp-releasetool -pyparsing==3.0.9 \ - --hash=sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb \ - --hash=sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc - # via packaging pyperclip==1.8.2 \ --hash=sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57 # via gcp-releasetool @@ -418,9 +442,9 @@ python-dateutil==2.8.2 \ --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 # via gcp-releasetool -readme-renderer==37.3 \ - --hash=sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273 \ - --hash=sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343 +readme-renderer==42.0 \ + --hash=sha256:13d039515c1f24de668e2c93f2e877b9dbe6c6c32328b90a40a49d8b2b85f36d \ + --hash=sha256:2d55489f83be4992fe4454939d1a051c33edbab778e82761d060c9fc6b308cd1 # via twine requests==2.31.0 \ --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \ @@ -431,17 +455,17 @@ requests==2.31.0 \ # google-cloud-storage # requests-toolbelt # twine -requests-toolbelt==0.10.1 \ - --hash=sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7 \ - --hash=sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d +requests-toolbelt==1.0.0 \ + --hash=sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6 \ + --hash=sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06 # via twine rfc3986==2.0.0 \ --hash=sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd \ --hash=sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c # via twine -rich==12.6.0 \ - --hash=sha256:a4eb26484f2c82589bd9a17c73d32a010b1e29d89f1604cd9bf3a2097b81bb5e \ - --hash=sha256:ba3a3775974105c221d31141f2c116f4fd65c5ceb0698657a11e9f295ec93fd0 +rich==13.6.0 \ + --hash=sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245 \ + --hash=sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef # via twine rsa==4.9 \ --hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \ @@ -455,43 +479,37 @@ six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 # via - # bleach # gcp-docuploader - # google-auth # python-dateutil -twine==4.0.1 \ - --hash=sha256:42026c18e394eac3e06693ee52010baa5313e4811d5a11050e7d48436cf41b9e \ - --hash=sha256:96b1cf12f7ae611a4a40b6ae8e9570215daff0611828f5fe1f37a16255ab24a0 +twine==4.0.2 \ + --hash=sha256:929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8 \ + --hash=sha256:9e102ef5fdd5a20661eb88fad46338806c3bd32cf1db729603fe3697b1bc83c8 # via -r requirements.in -typing-extensions==4.4.0 \ - --hash=sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa \ - --hash=sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e +typing-extensions==4.8.0 \ + --hash=sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0 \ + --hash=sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef # via -r requirements.in -urllib3==1.26.18 \ - --hash=sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07 \ - --hash=sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0 +urllib3==2.0.7 \ + --hash=sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84 \ + --hash=sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e # via # requests # twine -virtualenv==20.16.7 \ - --hash=sha256:8691e3ff9387f743e00f6bb20f70121f5e4f596cae754531f2b3b3a1b1ac696e \ - --hash=sha256:efd66b00386fdb7dbe4822d172303f40cd05e50e01740b19ea42425cbe653e29 +virtualenv==20.24.6 \ + --hash=sha256:02ece4f56fbf939dbbc33c0715159951d6bf14aaf5457b092e4548e1382455af \ + --hash=sha256:520d056652454c5098a00c0f073611ccbea4c79089331f60bf9d7ba247bb7381 # via nox -webencodings==0.5.1 \ - --hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \ - --hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923 - # via bleach -wheel==0.38.4 \ - --hash=sha256:965f5259b566725405b05e7cf774052044b1ed30119b5d586b2703aafe8719ac \ - --hash=sha256:b60533f3f5d530e971d6737ca6d58681ee434818fab630c83a734bb10c083ce8 +wheel==0.41.3 \ + --hash=sha256:488609bc63a29322326e05560731bf7bfea8e48ad646e1f5e40d366607de0942 \ + --hash=sha256:4d4987ce51a49370ea65c0bfd2234e8ce80a12780820d9dc462597a6e60d0841 # via -r requirements.in -zipp==3.10.0 \ - --hash=sha256:4fcb6f278987a6605757302a6e40e896257570d11c51628968ccb2a47e80c6c1 \ - --hash=sha256:7a7262fd930bd3e36c50b9a64897aec3fafff3dfdeec9623ae22b40e93f99bb8 +zipp==3.17.0 \ + --hash=sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31 \ + --hash=sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: -setuptools==65.5.1 \ - --hash=sha256:d0b9a8433464d5800cbe05094acf5c6d52a91bfac9b52bcfc4d41382be5d5d31 \ - --hash=sha256:e197a19aa8ec9722928f2206f8de752def0e4c9fc6953527360d1c36d94ddb2f +setuptools==68.2.2 \ + --hash=sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87 \ + --hash=sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a # via -r requirements.in From 182fe4e2d295768aaf016f94cb43b6b1e5572ebd Mon Sep 17 00:00:00 2001 From: "Bob \"Wombat\" Hogg" Date: Fri, 24 Nov 2023 11:46:59 -0500 Subject: [PATCH 06/21] docs: Show how to use named databases (#932) --- docs/index.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/index.rst b/docs/index.rst index ff5ec5fc..1e876df0 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -74,6 +74,18 @@ APIs and Services". From there, look for "Databases" in the Category filter. Make sure that both "Cloud Datastore API" and "Google Cloud Firestore API" are enabled. +Accessing a specific project, database, or namespace +==================================================== + +A client can be bound to a chosen Google Cloud project, database, and/or namespace +by passing one or more of these options to the client constructor:: + + client = ndb.Client( + project="your-project-id", + database="your-database-id", + namespace="your-namespace" + ) + Defining Entities, Keys, and Properties ======================================= From 42b3f0137caed25ac3242435b571155d2d84c78e Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Fri, 24 Nov 2023 15:28:14 -0500 Subject: [PATCH 07/21] feat: add support for google.cloud.ndb.__version__ (#929) --- google/cloud/ndb/__init__.py | 5 +++-- google/cloud/ndb/version.py | 15 +++++++++++++++ setup.py | 12 +++++++++++- 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 google/cloud/ndb/version.py diff --git a/google/cloud/ndb/__init__.py b/google/cloud/ndb/__init__.py index fa82bf91..3375db72 100644 --- a/google/cloud/ndb/__init__.py +++ b/google/cloud/ndb/__init__.py @@ -21,9 +21,9 @@ .. autodata:: __all__ """ -from pkg_resources import get_distribution +from google.cloud.ndb import version -__version__ = get_distribution("google-cloud-ndb").version +__version__ = version.__version__ from google.cloud.ndb.client import Client from google.cloud.ndb.context import AutoBatcher @@ -131,6 +131,7 @@ from google.cloud.ndb._transaction import non_transactional __all__ = [ + "__version__", "AutoBatcher", "Client", "Context", diff --git a/google/cloud/ndb/version.py b/google/cloud/ndb/version.py new file mode 100644 index 00000000..210b9c03 --- /dev/null +++ b/google/cloud/ndb/version.py @@ -0,0 +1,15 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +__version__ = "2.2.2" diff --git a/setup.py b/setup.py index 32e55010..8d9c8182 100644 --- a/setup.py +++ b/setup.py @@ -14,10 +14,20 @@ import io import os +import re import setuptools +PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__)) + +version = None + +with open(os.path.join(PACKAGE_ROOT, "google/cloud/ndb/version.py")) as fp: + version_candidates = re.findall(r"(?<=\")\d+.\d+.\d+(?=\")", fp.read()) + assert len(version_candidates) == 1 + version = version_candidates[0] + def main(): package_root = os.path.abspath(os.path.dirname(__file__)) readme_filename = os.path.join(package_root, "README.md") @@ -36,7 +46,7 @@ def main(): setuptools.setup( name="google-cloud-ndb", - version = "2.2.2", + version = version, description="NDB library for Google Cloud Datastore", long_description=readme, long_description_content_type="text/markdown", From ccae387720a28db2686e69dfe23a2599fc4908f0 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Thu, 25 Jan 2024 13:01:04 -0500 Subject: [PATCH 08/21] feat: Introduce compatibility with native namespace packages (#933) --- google/__init__.py | 22 --------------------- google/cloud/__init__.py | 22 --------------------- setup.py | 9 +++++++-- tests/unit/test_packaging.py | 37 ++++++++++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 46 deletions(-) delete mode 100644 google/__init__.py delete mode 100644 google/cloud/__init__.py create mode 100644 tests/unit/test_packaging.py diff --git a/google/__init__.py b/google/__init__.py deleted file mode 100644 index dd3a9f48..00000000 --- a/google/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -try: - import pkg_resources - - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - - __path__ = pkgutil.extend_path(__path__, __name__) diff --git a/google/cloud/__init__.py b/google/cloud/__init__.py deleted file mode 100644 index dd3a9f48..00000000 --- a/google/cloud/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2018 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -try: - import pkg_resources - - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - - __path__ = pkgutil.extend_path(__path__, __name__) diff --git a/setup.py b/setup.py index 8d9c8182..6479bce4 100644 --- a/setup.py +++ b/setup.py @@ -28,6 +28,12 @@ assert len(version_candidates) == 1 version = version_candidates[0] +packages = [ + package + for package in setuptools.find_namespace_packages() + if package.startswith("google") +] + def main(): package_root = os.path.abspath(os.path.dirname(__file__)) readme_filename = os.path.join(package_root, "README.md") @@ -73,8 +79,7 @@ def main(): "Topic :: Internet", ], platforms="Posix; MacOS X; Windows", - packages=setuptools.find_packages(), - namespace_packages=["google", "google.cloud"], + packages=packages, install_requires=dependencies, extras_require={}, python_requires=">=3.7", diff --git a/tests/unit/test_packaging.py b/tests/unit/test_packaging.py new file mode 100644 index 00000000..2e7aa97a --- /dev/null +++ b/tests/unit/test_packaging.py @@ -0,0 +1,37 @@ +# Copyright 2023 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import subprocess +import sys + + +def test_namespace_package_compat(tmp_path): + # The ``google`` namespace package should not be masked + # by the presence of ``google-cloud-ndb``. + google = tmp_path / "google" + google.mkdir() + google.joinpath("othermod.py").write_text("") + env = dict(os.environ, PYTHONPATH=str(tmp_path)) + cmd = [sys.executable, "-m", "google.othermod"] + subprocess.check_call(cmd, env=env) + + # The ``google.cloud`` namespace package should not be masked + # by the presence of ``google-cloud-ndb``. + google_cloud = tmp_path / "google" / "cloud" + google_cloud.mkdir() + google_cloud.joinpath("othermod.py").write_text("") + env = dict(os.environ, PYTHONPATH=str(tmp_path)) + cmd = [sys.executable, "-m", "google.cloud.othermod"] + subprocess.check_call(cmd, env=env) From 3cb27557e7f7e469be3414bb412973b78433830e Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 11:16:55 -0500 Subject: [PATCH 09/21] build(python): fix `docs` and `docfx` builds (#939) * build(python): fix `docs` and `docfx` builds Source-Link: https://github.com/googleapis/synthtool/commit/fac8444edd5f5526e804c306b766a271772a3e2f Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:5ea6d0ab82c956b50962f91d94e206d3921537ae5fe1549ec5326381d8905cfa * remove editable install to fix tests * See https://github.com/googleapis/synthtool/pull/1916 * See https://github.com/googleapis/synthtool/pull/1916 * exclude .nox from coverage * feat: Introduce compatibility with native namespace packages * refactor unit test and coverage tests --------- Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- .coveragerc | 1 + .github/.OwlBot.lock.yaml | 6 +- .gitignore | 4 + .kokoro/noxfile.py | 2 +- .kokoro/requirements.txt | 54 +++++----- .kokoro/samples/python3.12/common.cfg | 40 +++++++ .kokoro/samples/python3.12/continuous.cfg | 6 ++ .kokoro/samples/python3.12/periodic-head.cfg | 11 ++ .kokoro/samples/python3.12/periodic.cfg | 6 ++ .kokoro/samples/python3.12/presubmit.cfg | 6 ++ noxfile.py | 107 ++++++++++++------- 11 files changed, 176 insertions(+), 67 deletions(-) create mode 100644 .kokoro/samples/python3.12/common.cfg create mode 100644 .kokoro/samples/python3.12/continuous.cfg create mode 100644 .kokoro/samples/python3.12/periodic-head.cfg create mode 100644 .kokoro/samples/python3.12/periodic.cfg create mode 100644 .kokoro/samples/python3.12/presubmit.cfg diff --git a/.coveragerc b/.coveragerc index 40f596d9..1cee855b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -8,6 +8,7 @@ exclude_lines = # Re-enable the standard pragma pragma: NO COVER omit = + .nox/* */gapic/*.py */proto/*.py tests/*/*.py diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 453b540c..d8a1bbca 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -1,4 +1,4 @@ -# Copyright 2023 Google LLC +# Copyright 2024 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -13,5 +13,5 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:caffe0a9277daeccc4d1de5c9b55ebba0901b57c2f713ec9c876b0d4ec064f61 -# created: 2023-11-08T19:46:45.022803742Z + digest: sha256:5ea6d0ab82c956b50962f91d94e206d3921537ae5fe1549ec5326381d8905cfa +# created: 2024-01-15T16:32:08.142785673Z diff --git a/.gitignore b/.gitignore index 229f58f5..63022fac 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,7 @@ htmlcov # Built documentation docs/_build + +# Test logs +coverage.xml +*sponge_log.xml diff --git a/.kokoro/noxfile.py b/.kokoro/noxfile.py index 7c8a6399..483b5590 100644 --- a/.kokoro/noxfile.py +++ b/.kokoro/noxfile.py @@ -89,7 +89,7 @@ def get_pytest_env_vars() -> Dict[str, str]: # DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 8957e211..bb3d6ca3 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -93,30 +93,30 @@ colorlog==6.7.0 \ # via # gcp-docuploader # nox -cryptography==41.0.5 \ - --hash=sha256:0c327cac00f082013c7c9fb6c46b7cc9fa3c288ca702c74773968173bda421bf \ - --hash=sha256:0d2a6a598847c46e3e321a7aef8af1436f11c27f1254933746304ff014664d84 \ - --hash=sha256:227ec057cd32a41c6651701abc0328135e472ed450f47c2766f23267b792a88e \ - --hash=sha256:22892cc830d8b2c89ea60148227631bb96a7da0c1b722f2aac8824b1b7c0b6b8 \ - --hash=sha256:392cb88b597247177172e02da6b7a63deeff1937fa6fec3bbf902ebd75d97ec7 \ - --hash=sha256:3be3ca726e1572517d2bef99a818378bbcf7d7799d5372a46c79c29eb8d166c1 \ - --hash=sha256:573eb7128cbca75f9157dcde974781209463ce56b5804983e11a1c462f0f4e88 \ - --hash=sha256:580afc7b7216deeb87a098ef0674d6ee34ab55993140838b14c9b83312b37b86 \ - --hash=sha256:5a70187954ba7292c7876734183e810b728b4f3965fbe571421cb2434d279179 \ - --hash=sha256:73801ac9736741f220e20435f84ecec75ed70eda90f781a148f1bad546963d81 \ - --hash=sha256:7d208c21e47940369accfc9e85f0de7693d9a5d843c2509b3846b2db170dfd20 \ - --hash=sha256:8254962e6ba1f4d2090c44daf50a547cd5f0bf446dc658a8e5f8156cae0d8548 \ - --hash=sha256:88417bff20162f635f24f849ab182b092697922088b477a7abd6664ddd82291d \ - --hash=sha256:a48e74dad1fb349f3dc1d449ed88e0017d792997a7ad2ec9587ed17405667e6d \ - --hash=sha256:b948e09fe5fb18517d99994184854ebd50b57248736fd4c720ad540560174ec5 \ - --hash=sha256:c707f7afd813478e2019ae32a7c49cd932dd60ab2d2a93e796f68236b7e1fbf1 \ - --hash=sha256:d38e6031e113b7421db1de0c1b1f7739564a88f1684c6b89234fbf6c11b75147 \ - --hash=sha256:d3977f0e276f6f5bf245c403156673db103283266601405376f075c849a0b936 \ - --hash=sha256:da6a0ff8f1016ccc7477e6339e1d50ce5f59b88905585f77193ebd5068f1e797 \ - --hash=sha256:e270c04f4d9b5671ebcc792b3ba5d4488bf7c42c3c241a3748e2599776f29696 \ - --hash=sha256:e886098619d3815e0ad5790c973afeee2c0e6e04b4da90b88e6bd06e2a0b1b72 \ - --hash=sha256:ec3b055ff8f1dce8e6ef28f626e0972981475173d7973d63f271b29c8a2897da \ - --hash=sha256:fba1e91467c65fe64a82c689dc6cf58151158993b13eb7a7f3f4b7f395636723 +cryptography==41.0.6 \ + --hash=sha256:068bc551698c234742c40049e46840843f3d98ad7ce265fd2bd4ec0d11306596 \ + --hash=sha256:0f27acb55a4e77b9be8d550d762b0513ef3fc658cd3eb15110ebbcbd626db12c \ + --hash=sha256:2132d5865eea673fe6712c2ed5fb4fa49dba10768bb4cc798345748380ee3660 \ + --hash=sha256:3288acccef021e3c3c10d58933f44e8602cf04dba96d9796d70d537bb2f4bbc4 \ + --hash=sha256:35f3f288e83c3f6f10752467c48919a7a94b7d88cc00b0668372a0d2ad4f8ead \ + --hash=sha256:398ae1fc711b5eb78e977daa3cbf47cec20f2c08c5da129b7a296055fbb22aed \ + --hash=sha256:422e3e31d63743855e43e5a6fcc8b4acab860f560f9321b0ee6269cc7ed70cc3 \ + --hash=sha256:48783b7e2bef51224020efb61b42704207dde583d7e371ef8fc2a5fb6c0aabc7 \ + --hash=sha256:4d03186af98b1c01a4eda396b137f29e4e3fb0173e30f885e27acec8823c1b09 \ + --hash=sha256:5daeb18e7886a358064a68dbcaf441c036cbdb7da52ae744e7b9207b04d3908c \ + --hash=sha256:60e746b11b937911dc70d164060d28d273e31853bb359e2b2033c9e93e6f3c43 \ + --hash=sha256:742ae5e9a2310e9dade7932f9576606836ed174da3c7d26bc3d3ab4bd49b9f65 \ + --hash=sha256:7e00fb556bda398b99b0da289ce7053639d33b572847181d6483ad89835115f6 \ + --hash=sha256:85abd057699b98fce40b41737afb234fef05c67e116f6f3650782c10862c43da \ + --hash=sha256:8efb2af8d4ba9dbc9c9dd8f04d19a7abb5b49eab1f3694e7b5a16a5fc2856f5c \ + --hash=sha256:ae236bb8760c1e55b7a39b6d4d32d2279bc6c7c8500b7d5a13b6fb9fc97be35b \ + --hash=sha256:afda76d84b053923c27ede5edc1ed7d53e3c9f475ebaf63c68e69f1403c405a8 \ + --hash=sha256:b27a7fd4229abef715e064269d98a7e2909ebf92eb6912a9603c7e14c181928c \ + --hash=sha256:b648fe2a45e426aaee684ddca2632f62ec4613ef362f4d681a9a6283d10e079d \ + --hash=sha256:c5a550dc7a3b50b116323e3d376241829fd326ac47bc195e04eb33a8170902a9 \ + --hash=sha256:da46e2b5df770070412c46f87bac0849b8d685c5f2679771de277a422c7d0b86 \ + --hash=sha256:f39812f70fc5c71a15aa3c97b2bbe213c3f2a460b79bd21c40d033bb34a9bf36 \ + --hash=sha256:ff369dd19e8fe0528b02e8df9f2aeb2479f89b1270d90f96a63500afe9af5cae # via # gcp-releasetool # secretstorage @@ -263,9 +263,9 @@ jeepney==0.8.0 \ # via # keyring # secretstorage -jinja2==3.1.2 \ - --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \ - --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61 +jinja2==3.1.3 \ + --hash=sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa \ + --hash=sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90 # via gcp-releasetool keyring==24.2.0 \ --hash=sha256:4901caaf597bfd3bbd78c9a0c7c4c29fcd8310dab2cffefe749e916b6527acd6 \ diff --git a/.kokoro/samples/python3.12/common.cfg b/.kokoro/samples/python3.12/common.cfg new file mode 100644 index 00000000..0a43c6bb --- /dev/null +++ b/.kokoro/samples/python3.12/common.cfg @@ -0,0 +1,40 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "py-3.12" +} + +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-312" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-ndb/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-ndb/.kokoro/trampoline_v2.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.12/continuous.cfg b/.kokoro/samples/python3.12/continuous.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/python3.12/continuous.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.12/periodic-head.cfg b/.kokoro/samples/python3.12/periodic-head.cfg new file mode 100644 index 00000000..2710a244 --- /dev/null +++ b/.kokoro/samples/python3.12/periodic-head.cfg @@ -0,0 +1,11 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-ndb/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/samples/python3.12/periodic.cfg b/.kokoro/samples/python3.12/periodic.cfg new file mode 100644 index 00000000..71cd1e59 --- /dev/null +++ b/.kokoro/samples/python3.12/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} diff --git a/.kokoro/samples/python3.12/presubmit.cfg b/.kokoro/samples/python3.12/presubmit.cfg new file mode 100644 index 00000000..a1c8d975 --- /dev/null +++ b/.kokoro/samples/python3.12/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/noxfile.py b/noxfile.py index 6b2580ae..73b82358 100644 --- a/noxfile.py +++ b/noxfile.py @@ -30,52 +30,64 @@ CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() BLACK_VERSION = "black==22.3.0" +UNIT_TEST_STANDARD_DEPENDENCIES = [ + "mock", + "asyncmock", + "pytest", + "pytest-cov", + "google-cloud-testutils", + "google-cloud-core", +] def get_path(*names): return os.path.join(NOX_DIR, *names) -@nox.session(py=ALL_INTERPRETERS) -def unit(session): +def install_unittest_dependencies(session, *constraints): + standard_deps = UNIT_TEST_STANDARD_DEPENDENCIES + session.install(*standard_deps, *constraints) + session.install("-e", ".", *constraints) + + +def default(session): + # Install all test dependencies, then install this package in-place. constraints_path = str( CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" ) - # Install all dependencies. - session.install("pytest", "pytest-cov") - session.install("google-cloud-testutils", "-c", constraints_path) - session.install("-e", ".", "-c", constraints_path) - # This variable is used to skip coverage by Python version - session.env["PY_VERSION"] = session.python[0] + install_unittest_dependencies(session, "-c", constraints_path) # Run py.test against the unit tests. - run_args = ["pytest"] - if session.posargs: - run_args.extend(session.posargs) - else: - run_args.extend( - [ - "--cov=google.cloud.ndb", - "--cov=unit", - "--cov-append", - "--cov-config", - get_path(".coveragerc"), - "--cov-report=term-missing", - ] - ) - run_args.append(get_path("tests", "unit")) - session.run(*run_args) + session.run( + "py.test", + "--quiet", + f"--junitxml=unit_{session.python}_sponge_log.xml", + "--cov=google", + "--cov=tests/unit", + "--cov-append", + "--cov-config=.coveragerc", + "--cov-report=", + "--cov-fail-under=0", + os.path.join("tests", "unit"), + *session.posargs, + ) + - if not session.posargs: - session.notify("cover") +@nox.session(python=ALL_INTERPRETERS) +def unit(session): + """Run the unit test suite.""" + default(session) @nox.session(py=DEFAULT_INTERPRETER) def cover(session): - # Install all dependencies. - session.install("coverage") - # Run coverage report. - session.run("coverage", "report", "--fail-under=100", "--show-missing") - # Erase cached coverage data. + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.install("coverage", "pytest-cov") + session.run("coverage", "report", "--show-missing", "--fail-under=100") + session.run("coverage", "erase") @@ -119,9 +131,21 @@ def blacken(session): def docs(session): """Build the docs for this library.""" - session.install("-e", ".") + session.install(".") session.install( - "Sphinx==4.0.1", "alabaster", "recommonmark", "sphinxcontrib.spelling" + # We need to pin to specific versions of the `sphinxcontrib-*` packages + # which still support sphinx 4.x. + # See https://github.com/googleapis/sphinx-docfx-yaml/issues/344 + # and https://github.com/googleapis/sphinx-docfx-yaml/issues/345. + "sphinxcontrib-applehelp==1.0.4", + "sphinxcontrib-devhelp==1.0.2", + "sphinxcontrib-htmlhelp==2.0.1", + "sphinxcontrib-qthelp==1.0.3", + "sphinxcontrib-serializinghtml==1.1.5", + "sphinx==4.0.1", + "alabaster", + "recommonmark", + "sphinxcontrib.spelling", ) shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) @@ -142,8 +166,19 @@ def docs(session): @nox.session(py="3.9") def doctest(session): # Install all dependencies. - session.install("Sphinx==4.0.1") - session.install("sphinxcontrib.spelling") + session.install( + # We need to pin to specific versions of the `sphinxcontrib-*` packages + # which still support sphinx 4.x. + # See https://github.com/googleapis/sphinx-docfx-yaml/issues/344 + # and https://github.com/googleapis/sphinx-docfx-yaml/issues/345. + "sphinxcontrib-applehelp==1.0.4", + "sphinxcontrib-devhelp==1.0.2", + "sphinxcontrib-htmlhelp==2.0.1", + "sphinxcontrib-qthelp==1.0.3", + "sphinxcontrib-serializinghtml==1.1.5", + "sphinx==4.0.1", + "sphinxcontrib.spelling", + ) session.install(".") # Run the script for building docs and running doctests. run_args = [ @@ -190,7 +225,7 @@ def system(session): session.install("google-cloud-testutils") for local_dep in LOCAL_DEPS: session.install(local_dep) - session.install("-e", ".", "-c", constraints_path) + session.install(".", "-c", constraints_path) # Run py.test against the system tests. if system_test_exists: From 51038139e45807b3a14346ded702fbe202dcfdf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz?= Date: Fri, 9 Feb 2024 16:59:55 +0100 Subject: [PATCH 10/21] docs: fix a mistaken ID description (#943) --- google/cloud/ndb/key.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google/cloud/ndb/key.py b/google/cloud/ndb/key.py index b2919159..04b1c1ff 100644 --- a/google/cloud/ndb/key.py +++ b/google/cloud/ndb/key.py @@ -636,7 +636,7 @@ def string_id(self): return self._key.name def integer_id(self): - """The string ID in the last ``(kind, id)`` pair, if any. + """The integer ID in the last ``(kind, id)`` pair, if any. .. doctest:: key-integer-id From 5cc6e918a6fe00fbac336fbcd295162f9dc53918 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 23 Feb 2024 11:53:13 -0800 Subject: [PATCH 11/21] build(deps): bump cryptography from 42.0.0 to 42.0.2 in .kokoro (#947) Source-Link: https://github.com/googleapis/synthtool/commit/8d392a55db44b00b4a9b995318051e334eecdcf1 Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:a0c4463fcfd9893fc172a3b3db2b6ac0c7b94ec6ad458c7dcea12d9693615ac3 Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 +-- .kokoro/requirements.txt | 57 ++++++++++++++++++++++----------------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index d8a1bbca..51213ca0 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,5 +13,5 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:5ea6d0ab82c956b50962f91d94e206d3921537ae5fe1549ec5326381d8905cfa -# created: 2024-01-15T16:32:08.142785673Z + digest: sha256:a0c4463fcfd9893fc172a3b3db2b6ac0c7b94ec6ad458c7dcea12d9693615ac3 +# created: 2024-02-17T12:21:23.177926195Z diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index bb3d6ca3..f80bdcd6 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -93,30 +93,39 @@ colorlog==6.7.0 \ # via # gcp-docuploader # nox -cryptography==41.0.6 \ - --hash=sha256:068bc551698c234742c40049e46840843f3d98ad7ce265fd2bd4ec0d11306596 \ - --hash=sha256:0f27acb55a4e77b9be8d550d762b0513ef3fc658cd3eb15110ebbcbd626db12c \ - --hash=sha256:2132d5865eea673fe6712c2ed5fb4fa49dba10768bb4cc798345748380ee3660 \ - --hash=sha256:3288acccef021e3c3c10d58933f44e8602cf04dba96d9796d70d537bb2f4bbc4 \ - --hash=sha256:35f3f288e83c3f6f10752467c48919a7a94b7d88cc00b0668372a0d2ad4f8ead \ - --hash=sha256:398ae1fc711b5eb78e977daa3cbf47cec20f2c08c5da129b7a296055fbb22aed \ - --hash=sha256:422e3e31d63743855e43e5a6fcc8b4acab860f560f9321b0ee6269cc7ed70cc3 \ - --hash=sha256:48783b7e2bef51224020efb61b42704207dde583d7e371ef8fc2a5fb6c0aabc7 \ - --hash=sha256:4d03186af98b1c01a4eda396b137f29e4e3fb0173e30f885e27acec8823c1b09 \ - --hash=sha256:5daeb18e7886a358064a68dbcaf441c036cbdb7da52ae744e7b9207b04d3908c \ - --hash=sha256:60e746b11b937911dc70d164060d28d273e31853bb359e2b2033c9e93e6f3c43 \ - --hash=sha256:742ae5e9a2310e9dade7932f9576606836ed174da3c7d26bc3d3ab4bd49b9f65 \ - --hash=sha256:7e00fb556bda398b99b0da289ce7053639d33b572847181d6483ad89835115f6 \ - --hash=sha256:85abd057699b98fce40b41737afb234fef05c67e116f6f3650782c10862c43da \ - --hash=sha256:8efb2af8d4ba9dbc9c9dd8f04d19a7abb5b49eab1f3694e7b5a16a5fc2856f5c \ - --hash=sha256:ae236bb8760c1e55b7a39b6d4d32d2279bc6c7c8500b7d5a13b6fb9fc97be35b \ - --hash=sha256:afda76d84b053923c27ede5edc1ed7d53e3c9f475ebaf63c68e69f1403c405a8 \ - --hash=sha256:b27a7fd4229abef715e064269d98a7e2909ebf92eb6912a9603c7e14c181928c \ - --hash=sha256:b648fe2a45e426aaee684ddca2632f62ec4613ef362f4d681a9a6283d10e079d \ - --hash=sha256:c5a550dc7a3b50b116323e3d376241829fd326ac47bc195e04eb33a8170902a9 \ - --hash=sha256:da46e2b5df770070412c46f87bac0849b8d685c5f2679771de277a422c7d0b86 \ - --hash=sha256:f39812f70fc5c71a15aa3c97b2bbe213c3f2a460b79bd21c40d033bb34a9bf36 \ - --hash=sha256:ff369dd19e8fe0528b02e8df9f2aeb2479f89b1270d90f96a63500afe9af5cae +cryptography==42.0.2 \ + --hash=sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380 \ + --hash=sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589 \ + --hash=sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea \ + --hash=sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65 \ + --hash=sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a \ + --hash=sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3 \ + --hash=sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008 \ + --hash=sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1 \ + --hash=sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2 \ + --hash=sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635 \ + --hash=sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2 \ + --hash=sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90 \ + --hash=sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee \ + --hash=sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a \ + --hash=sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242 \ + --hash=sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12 \ + --hash=sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2 \ + --hash=sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d \ + --hash=sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be \ + --hash=sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee \ + --hash=sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6 \ + --hash=sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529 \ + --hash=sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929 \ + --hash=sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1 \ + --hash=sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6 \ + --hash=sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a \ + --hash=sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446 \ + --hash=sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9 \ + --hash=sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888 \ + --hash=sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4 \ + --hash=sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33 \ + --hash=sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f # via # gcp-releasetool # secretstorage From b5c847783b80071c2dd9e9a3dbf899230c99e64a Mon Sep 17 00:00:00 2001 From: Jim Morrison Date: Mon, 26 Feb 2024 17:53:14 -0800 Subject: [PATCH 12/21] feat: Add Python 3.12 (#949) * chore(python): Add Python 3.12 * feat: Add Python 3.12 * Allow extra log message in cache tests. --- CONTRIBUTING.rst | 4 +++- noxfile.py | 2 +- setup.py | 1 + testing/constraints-3.12.txt | 0 tests/unit/test__cache.py | 4 ++-- tests/unit/test__datastore_api.py | 24 ++++++++++++++++++------ 6 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 testing/constraints-3.12.txt diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 8cee1114..2baa8674 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -24,7 +24,7 @@ In order to add a feature to ``python-ndb``: documentation (in ``docs/``). - The feature must work fully on the following CPython versions: - 3.7, 3.8, 3.9, 3.10, and 3.11 on both UNIX and Windows. + 3.7, 3.8, 3.9, 3.10, 3.11 and 3.12 on both UNIX and Windows. - The feature must not add unnecessary dependencies (where "unnecessary" is of course subjective, but new dependencies should @@ -260,12 +260,14 @@ We support: - `Python 3.9`_ - `Python 3.10`_ - `Python 3.11`_ +- `Python 3.12`_ .. _Python 3.7: https://docs.python.org/3.7/ .. _Python 3.8: https://docs.python.org/3.8/ .. _Python 3.9: https://docs.python.org/3.9/ .. _Python 3.10: https://docs.python.org/3.10/ .. _Python 3.11: https://docs.python.org/3.11/ +.. _Python 3.12: https://docs.python.org/3.12/ Supported versions can be found in our ``noxfile.py`` `config`_. diff --git a/noxfile.py b/noxfile.py index 73b82358..2c6bbcb5 100644 --- a/noxfile.py +++ b/noxfile.py @@ -26,7 +26,7 @@ LOCAL_DEPS = ("google-api-core", "google-cloud-core") NOX_DIR = os.path.abspath(os.path.dirname(__file__)) DEFAULT_INTERPRETER = "3.8" -ALL_INTERPRETERS = ("3.7", "3.8", "3.9", "3.10", "3.11") +ALL_INTERPRETERS = ("3.7", "3.8", "3.9", "3.10", "3.11", "3.12") CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() BLACK_VERSION = "black==22.3.0" diff --git a/setup.py b/setup.py index 6479bce4..d5c32763 100644 --- a/setup.py +++ b/setup.py @@ -75,6 +75,7 @@ def main(): "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Operating System :: OS Independent", "Topic :: Internet", ], diff --git a/testing/constraints-3.12.txt b/testing/constraints-3.12.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/test__cache.py b/tests/unit/test__cache.py index b812c95b..c0b3e426 100644 --- a/tests/unit/test__cache.py +++ b/tests/unit/test__cache.py @@ -178,7 +178,7 @@ class TransientError(Exception): with warnings.catch_warnings(record=True) as logged: assert _cache.global_get(b"foo").result() is None - assert len(logged) == 1 + assert len(logged) in [1, 2] _batch.get_batch.assert_called_once_with(_cache._GlobalCacheGetBatch) batch.add.assert_called_once_with(b"foo") @@ -314,7 +314,7 @@ class TransientError(Exception): with warnings.catch_warnings(record=True) as logged: assert _cache.global_set(b"key", b"value").result() is None - assert len(logged) == 0 + assert len(logged) in [0, 1] _batch.get_batch.assert_called_once_with(_cache._GlobalCacheSetBatch, {}) batch.add.assert_called_once_with(b"key", b"value") diff --git a/tests/unit/test__datastore_api.py b/tests/unit/test__datastore_api.py index 783134b4..0db656a3 100644 --- a/tests/unit/test__datastore_api.py +++ b/tests/unit/test__datastore_api.py @@ -1253,7 +1253,9 @@ def test_wo_transaction(stub, datastore_pb2): ) request = datastore_pb2.CommitRequest.return_value - assert api.commit.future.called_once_with(request) + api.commit.future.assert_called_once_with( + request, metadata=mock.ANY, timeout=mock.ANY + ) @staticmethod @pytest.mark.usefixtures("in_context") @@ -1276,7 +1278,9 @@ def test_w_transaction(stub, datastore_pb2): ) request = datastore_pb2.CommitRequest.return_value - assert api.commit.future.called_once_with(request) + api.commit.future.assert_called_once_with( + request, metadata=mock.ANY, timeout=mock.ANY + ) @pytest.mark.usefixtures("in_context") @@ -1365,7 +1369,9 @@ def test__datastore_allocate_ids(stub, datastore_pb2): ) request = datastore_pb2.AllocateIdsRequest.return_value - assert api.allocate_ids.future.called_once_with(request) + api.allocate_ids.future.assert_called_once_with( + request, metadata=mock.ANY, timeout=mock.ANY + ) @pytest.mark.usefixtures("in_context") @@ -1407,7 +1413,9 @@ def test_read_only(stub, datastore_pb2): ) request = datastore_pb2.BeginTransactionRequest.return_value - assert api.begin_transaction.future.called_once_with(request) + api.begin_transaction.future.assert_called_once_with( + request, metadata=mock.ANY, timeout=mock.ANY + ) @staticmethod @pytest.mark.usefixtures("in_context") @@ -1432,7 +1440,9 @@ def test_read_write(stub, datastore_pb2): ) request = datastore_pb2.BeginTransactionRequest.return_value - assert api.begin_transaction.future.called_once_with(request) + api.begin_transaction.future.assert_called_once_with( + request, metadata=mock.ANY, timeout=mock.ANY + ) @pytest.mark.usefixtures("in_context") @@ -1463,7 +1473,9 @@ def test__datastore_rollback(stub, datastore_pb2): ) request = datastore_pb2.RollbackRequest.return_value - assert api.rollback.future.called_once_with(request) + api.rollback.future.assert_called_once_with( + request, metadata=mock.ANY, timeout=mock.ANY + ) def test__complete(): From 7e8481db84a6d0b96cf09c38e90f47d6b7847a0b Mon Sep 17 00:00:00 2001 From: Jim Morrison Date: Mon, 26 Feb 2024 20:55:34 -0800 Subject: [PATCH 13/21] docs: Correct read_consistency docs. (#948) --- google/cloud/ndb/query.py | 56 +++++++++++++++------------------------ 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/google/cloud/ndb/query.py b/google/cloud/ndb/query.py index 65b8f140..6a599630 100644 --- a/google/cloud/ndb/query.py +++ b/google/cloud/ndb/query.py @@ -1742,11 +1742,9 @@ def fetch(self, limit=None, **kwargs): end_cursor: Endpoint point for search. timeout (Optional[int]): Override the gRPC timeout, in seconds. deadline (Optional[int]): DEPRECATED: Synonym for ``timeout``. - read_consistency: If not in a transaction, defaults to - ``ndb.EVENTUAL`` for potentially faster query results without - having to wait for Datastore to apply pending changes to all - returned records. Otherwise consistency with current - transaction is maintained. + read_consistency: If set then passes the explicit read consistency to + the server. May not be set to ``ndb.EVENTUAL`` when a transaction + is specified. read_policy: DEPRECATED: Synonym for ``read_consistency``. transaction (bytes): Transaction ID to use for query. Results will be consistent with Datastore state for that transaction. @@ -1795,11 +1793,9 @@ def fetch_async(self, limit=None, **kwargs): end_cursor: Endpoint point for search. timeout (Optional[int]): Override the gRPC timeout, in seconds. deadline (Optional[int]): DEPRECATED: Synonym for ``timeout``. - read_consistency: If not in a transaction, defaults to - ``ndb.EVENTUAL`` for potentially faster query results without - having to wait for Datastore to apply pending changes to all - returned records. Otherwise consistency with current - transaction is maintained. + read_consistency: If set then passes the explicit read consistency to + the server. May not be set to ``ndb.EVENTUAL`` when a transaction + is specified. read_policy: DEPRECATED: Synonym for ``read_consistency``. transaction (bytes): Transaction ID to use for query. Results will be consistent with Datastore state for that transaction. @@ -1897,11 +1893,9 @@ def iter(self, **kwargs): end_cursor: Endpoint point for search. timeout (Optional[int]): Override the gRPC timeout, in seconds. deadline (Optional[int]): DEPRECATED: Synonym for ``timeout``. - read_consistency: If not in a transaction, defaults to - ``ndb.EVENTUAL`` for potentially faster query results without - having to wait for Datastore to apply pending changes to all - returned records. Otherwise consistency with current - transaction is maintained. + read_consistency: If set then passes the explicit read consistency to + the server. May not be set to ``ndb.EVENTUAL`` when a transaction + is specified. read_policy: DEPRECATED: Synonym for ``read_consistency``. transaction (bytes): Transaction ID to use for query. Results will be consistent with Datastore state for that transaction. @@ -1960,11 +1954,9 @@ def map(self, callback, **kwargs): end_cursor: Endpoint point for search. timeout (Optional[int]): Override the gRPC timeout, in seconds. deadline (Optional[int]): DEPRECATED: Synonym for ``timeout``. - read_consistency: If not in a transaction, defaults to - ``ndb.EVENTUAL`` for potentially faster query results without - having to wait for Datastore to apply pending changes to all - returned records. Otherwise consistency with current - transaction is maintained. + read_consistency: If set then passes the explicit read consistency to + the server. May not be set to ``ndb.EVENTUAL`` when a transaction + is specified. read_policy: DEPRECATED: Synonym for ``read_consistency``. transaction (bytes): Transaction ID to use for query. Results will be consistent with Datastore state for that transaction. @@ -2073,11 +2065,9 @@ def get(self, **kwargs): end_cursor: Endpoint point for search. timeout (Optional[int]): Override the gRPC timeout, in seconds. deadline (Optional[int]): DEPRECATED: Synonym for ``timeout``. - read_consistency: If not in a transaction, defaults to - ``ndb.EVENTUAL`` for potentially faster query results without - having to wait for Datastore to apply pending changes to all - returned records. Otherwise consistency with current - transaction is maintained. + read_consistency: If set then passes the explicit read consistency to + the server. May not be set to ``ndb.EVENTUAL`` when a transaction + is specified. read_policy: DEPRECATED: Synonym for ``read_consistency``. transaction (bytes): Transaction ID to use for query. Results will be consistent with Datastore state for that transaction. @@ -2178,11 +2168,9 @@ def count(self, limit=None, **kwargs): end_cursor: Endpoint point for search. timeout (Optional[int]): Override the gRPC timeout, in seconds. deadline (Optional[int]): DEPRECATED: Synonym for ``timeout``. - read_consistency: If not in a transaction, defaults to - ``ndb.EVENTUAL`` for potentially faster query results without - having to wait for Datastore to apply pending changes to all - returned records. Otherwise consistency with current - transaction is maintained. + read_consistency: If set then passes the explicit read consistency to + the server. May not be set to ``ndb.EVENTUAL`` when a transaction + is specified. read_policy: DEPRECATED: Synonym for ``read_consistency``. transaction (bytes): Transaction ID to use for query. Results will be consistent with Datastore state for that transaction. @@ -2272,11 +2260,9 @@ def fetch_page(self, page_size, **kwargs): end_cursor: Endpoint point for search. timeout (Optional[int]): Override the gRPC timeout, in seconds. deadline (Optional[int]): DEPRECATED: Synonym for ``timeout``. - read_consistency: If not in a transaction, defaults to - ``ndb.EVENTUAL`` for potentially faster query results without - having to wait for Datastore to apply pending changes to all - returned records. Otherwise consistency with current - transaction is maintained. + read_consistency: If set then passes the explicit read consistency to + the server. May not be set to ``ndb.EVENTUAL`` when a transaction + is specified. read_policy: DEPRECATED: Synonym for ``read_consistency``. transaction (bytes): Transaction ID to use for query. Results will be consistent with Datastore state for that transaction. From dab9edf0fc161051eb13c296cbe973b3a16b502d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20=C5=BBy=C5=BAniewski?= Date: Tue, 27 Feb 2024 21:46:58 +0100 Subject: [PATCH 14/21] fix: compressed repeated to uncompressed property (#772) --- google/cloud/ndb/model.py | 6 +++++- tests/unit/test_model.py | 31 +++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/google/cloud/ndb/model.py b/google/cloud/ndb/model.py index 42fe044b..224c6deb 100644 --- a/google/cloud/ndb/model.py +++ b/google/cloud/ndb/model.py @@ -2672,7 +2672,11 @@ def _from_datastore(self, ds_entity, value): if self._name in ds_entity._meanings: meaning = ds_entity._meanings[self._name][0] if meaning == _MEANING_COMPRESSED and not self._compressed: - value.b_val = zlib.decompress(value.b_val) + if self._repeated: + for sub_value in value: + sub_value.b_val = zlib.decompress(sub_value.b_val) + else: + value.b_val = zlib.decompress(value.b_val) return value def _db_set_compressed_meaning(self, p): diff --git a/tests/unit/test_model.py b/tests/unit/test_model.py index 6cb0ac90..5e6a11cb 100644 --- a/tests/unit/test_model.py +++ b/tests/unit/test_model.py @@ -1862,11 +1862,12 @@ class ThisKind(model.Model): compressed_value_one = zlib.compress(uncompressed_value_one) uncompressed_value_two = b"xyz" * 1000 compressed_value_two = zlib.compress(uncompressed_value_two) - datastore_entity.update({"foo": [compressed_value_one, compressed_value_two]}) + compressed_value = [compressed_value_one, compressed_value_two] + datastore_entity.update({"foo": compressed_value}) meanings = { "foo": ( model._MEANING_COMPRESSED, - [compressed_value_one, compressed_value_two], + compressed_value, ) } datastore_entity._meanings = meanings @@ -1875,6 +1876,32 @@ class ThisKind(model.Model): ds_entity = model._entity_to_ds_entity(entity) assert ds_entity["foo"] == [compressed_value_one, compressed_value_two] + @staticmethod + @pytest.mark.usefixtures("in_context") + def test__from_datastore_compressed_repeated_to_uncompressed(): + class ThisKind(model.Model): + foo = model.BlobProperty(compressed=False, repeated=True) + + key = datastore.Key("ThisKind", 123, project="testing") + datastore_entity = datastore.Entity(key=key) + uncompressed_value_one = b"abc" * 1000 + compressed_value_one = zlib.compress(uncompressed_value_one) + uncompressed_value_two = b"xyz" * 1000 + compressed_value_two = zlib.compress(uncompressed_value_two) + compressed_value = [compressed_value_one, compressed_value_two] + datastore_entity.update({"foo": compressed_value}) + meanings = { + "foo": ( + model._MEANING_COMPRESSED, + compressed_value, + ) + } + datastore_entity._meanings = meanings + protobuf = helpers.entity_to_protobuf(datastore_entity) + entity = model._entity_from_protobuf(protobuf) + ds_entity = model._entity_to_ds_entity(entity) + assert ds_entity["foo"] == [uncompressed_value_one, uncompressed_value_two] + @staticmethod @pytest.mark.usefixtures("in_context") def test__from_datastore_uncompressed_to_uncompressed(): From 106772f031f6c37500a0d463698e59008f9bf19a Mon Sep 17 00:00:00 2001 From: Jim Morrison Date: Tue, 27 Feb 2024 14:29:20 -0800 Subject: [PATCH 15/21] feat: Use server side != for queries. (#950) --- google/cloud/ndb/_datastore_query.py | 2 ++ google/cloud/ndb/query.py | 13 +++---------- tests/unit/test_model.py | 13 +++++-------- tests/unit/test_query.py | 14 +++++++++++--- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/google/cloud/ndb/_datastore_query.py b/google/cloud/ndb/_datastore_query.py index 90c32ba1..553c8285 100644 --- a/google/cloud/ndb/_datastore_query.py +++ b/google/cloud/ndb/_datastore_query.py @@ -56,6 +56,8 @@ "<=": query_pb2.PropertyFilter.Operator.LESS_THAN_OR_EQUAL, ">": query_pb2.PropertyFilter.Operator.GREATER_THAN, ">=": query_pb2.PropertyFilter.Operator.GREATER_THAN_OR_EQUAL, + "!=": query_pb2.PropertyFilter.Operator.NOT_EQUAL, + "IN": query_pb2.PropertyFilter.Operator.IN, } _KEY_NOT_IN_CACHE = object() diff --git a/google/cloud/ndb/query.py b/google/cloud/ndb/query.py index 6a599630..7fa46706 100644 --- a/google/cloud/ndb/query.py +++ b/google/cloud/ndb/query.py @@ -604,8 +604,6 @@ class FilterNode(Node): The constructor for this type may not always return a :class:`FilterNode`. For example: - * The filter ``name != value`` is converted into - ``(name > value) OR (name < value)`` (a :class:`DisjunctionNode`) * The filter ``name in (value1, ..., valueN)`` is converted into ``(name = value1) OR ... OR (name = valueN)`` (also a :class:`DisjunctionNode`) @@ -639,11 +637,6 @@ def __new__(cls, name, opsymbol, value): if isinstance(value, model.Key): value = value._key - if opsymbol == _NE_OP: - node1 = FilterNode(name, _LT_OP, value) - node2 = FilterNode(name, _GT_OP, value) - return DisjunctionNode(node1, node2) - if opsymbol == _IN_OP: if not isinstance(value, (list, tuple, set, frozenset)): raise TypeError( @@ -704,17 +697,17 @@ def _to_filter(self, post=False): representation of the filter. Raises: - NotImplementedError: If the ``opsymbol`` is ``!=`` or ``in``, since + NotImplementedError: If the ``opsymbol`` is ``in``, since they should correspond to a composite filter. This should never occur since the constructor will create ``OR`` nodes for - ``!=`` and ``in`` + ``in`` """ # Avoid circular import in Python 2.7 from google.cloud.ndb import _datastore_query if post: return None - if self._opsymbol in (_NE_OP, _IN_OP): + if self._opsymbol in (_IN_OP): raise NotImplementedError( "Inequality filters are not single filter " "expressions and therefore cannot be converted " diff --git a/tests/unit/test_model.py b/tests/unit/test_model.py index 5e6a11cb..3250d22d 100644 --- a/tests/unit/test_model.py +++ b/tests/unit/test_model.py @@ -479,15 +479,12 @@ def test___eq__(): def test___ne__(): prop = model.Property("name", indexed=True) value = 7.0 - expected = query_module.DisjunctionNode( - query_module.FilterNode("name", "<", value), - query_module.FilterNode("name", ">", value), - ) + expected = query_module.FilterNode("name", "!=", value) - or_node_left = prop != value - assert or_node_left == expected - or_node_right = value != prop - assert or_node_right == expected + ne_node_left = prop != value + assert ne_node_left == expected + ne_node_right = value != prop + assert ne_node_right == expected @staticmethod def test___lt__(): diff --git a/tests/unit/test_query.py b/tests/unit/test_query.py index df7df55a..589c9bcc 100644 --- a/tests/unit/test_query.py +++ b/tests/unit/test_query.py @@ -656,11 +656,14 @@ def test_constructor_in_invalid_container(): @staticmethod def test_constructor_ne(): - or_node = query_module.FilterNode("a", "!=", 2.5) + ne_node = query_module.FilterNode("a", "!=", 2.5) filter_node1 = query_module.FilterNode("a", "<", 2.5) filter_node2 = query_module.FilterNode("a", ">", 2.5) - assert or_node == query_module.DisjunctionNode(filter_node1, filter_node2) + assert ne_node != query_module.DisjunctionNode(filter_node1, filter_node2) + assert ne_node._value == 2.5 + assert ne_node._opsymbol == "!=" + assert ne_node._name == "a" @staticmethod def test_pickling(): @@ -693,10 +696,15 @@ def test__to_filter_post(): filter_node = query_module.FilterNode("speed", ">=", 88) assert filter_node._to_filter(post=True) is None + @staticmethod + def test__to_ne_filter_op(): + filter_node = query_module.FilterNode("speed", "!=", 88) + assert filter_node._to_filter(post=True) is None + @staticmethod def test__to_filter_bad_op(): filter_node = query_module.FilterNode("speed", ">=", 88) - filter_node._opsymbol = "!=" + filter_node._opsymbol = "in" with pytest.raises(NotImplementedError): filter_node._to_filter() From 2646cef3e2687461174a11c45f29de7b84d1fcdb Mon Sep 17 00:00:00 2001 From: Jim Morrison Date: Wed, 28 Feb 2024 09:14:57 -0800 Subject: [PATCH 16/21] feat: Allow queries using server side IN. (#954) * feat: Allow queries using server side IN. * Rename force_server to server_op. --- google/cloud/ndb/_datastore_query.py | 2 +- google/cloud/ndb/model.py | 4 ++-- google/cloud/ndb/query.py | 18 ++++----------- tests/system/test_query.py | 34 ++++++++++++++++++++++++++++ tests/unit/test_model.py | 29 +++++++++++++++++++++++- tests/unit/test_query.py | 7 ------ 6 files changed, 69 insertions(+), 25 deletions(-) diff --git a/google/cloud/ndb/_datastore_query.py b/google/cloud/ndb/_datastore_query.py index 553c8285..480a2a68 100644 --- a/google/cloud/ndb/_datastore_query.py +++ b/google/cloud/ndb/_datastore_query.py @@ -57,7 +57,7 @@ ">": query_pb2.PropertyFilter.Operator.GREATER_THAN, ">=": query_pb2.PropertyFilter.Operator.GREATER_THAN_OR_EQUAL, "!=": query_pb2.PropertyFilter.Operator.NOT_EQUAL, - "IN": query_pb2.PropertyFilter.Operator.IN, + "in": query_pb2.PropertyFilter.Operator.IN, } _KEY_NOT_IN_CACHE = object() diff --git a/google/cloud/ndb/model.py b/google/cloud/ndb/model.py index 224c6deb..b43d4163 100644 --- a/google/cloud/ndb/model.py +++ b/google/cloud/ndb/model.py @@ -1258,7 +1258,7 @@ def __ge__(self, value): """FilterNode: Represents the ``>=`` comparison.""" return self._comparison(">=", value) - def _IN(self, value): + def _IN(self, value, server_op=False): """For the ``in`` comparison operator. The ``in`` operator cannot be overloaded in the way we want @@ -1315,7 +1315,7 @@ def _IN(self, value): sub_value = self._datastore_type(sub_value) values.append(sub_value) - return query.FilterNode(self._name, "in", values) + return query.FilterNode(self._name, "in", values, server_op=server_op) IN = _IN """Used to check if a property value is contained in a set of values. diff --git a/google/cloud/ndb/query.py b/google/cloud/ndb/query.py index 7fa46706..6109fe11 100644 --- a/google/cloud/ndb/query.py +++ b/google/cloud/ndb/query.py @@ -619,6 +619,7 @@ class FilterNode(Node): opsymbol (str): The comparison operator. One of ``=``, ``!=``, ``<``, ``<=``, ``>``, ``>=`` or ``in``. value (Any): The value to filter on / relative to. + server_op (bool): Force the operator to use a server side filter. Raises: TypeError: If ``opsymbol`` is ``"in"`` but ``value`` is not a @@ -630,7 +631,7 @@ class FilterNode(Node): _opsymbol = None _value = None - def __new__(cls, name, opsymbol, value): + def __new__(cls, name, opsymbol, value, server_op=False): # Avoid circular import in Python 2.7 from google.cloud.ndb import model @@ -648,7 +649,8 @@ def __new__(cls, name, opsymbol, value): return FalseNode() if len(nodes) == 1: return nodes[0] - return DisjunctionNode(*nodes) + if not server_op: + return DisjunctionNode(*nodes) instance = super(FilterNode, cls).__new__(cls) instance._name = name @@ -695,24 +697,12 @@ def _to_filter(self, post=False): Optional[query_pb2.PropertyFilter]: Returns :data:`None`, if this is a post-filter, otherwise returns the protocol buffer representation of the filter. - - Raises: - NotImplementedError: If the ``opsymbol`` is ``in``, since - they should correspond to a composite filter. This should - never occur since the constructor will create ``OR`` nodes for - ``in`` """ # Avoid circular import in Python 2.7 from google.cloud.ndb import _datastore_query if post: return None - if self._opsymbol in (_IN_OP): - raise NotImplementedError( - "Inequality filters are not single filter " - "expressions and therefore cannot be converted " - "to a single filter ({!r})".format(self._opsymbol) - ) return _datastore_query.make_filter(self._name, self._opsymbol, self._value) diff --git a/tests/system/test_query.py b/tests/system/test_query.py index df00a6b6..fb2e9bbb 100644 --- a/tests/system/test_query.py +++ b/tests/system/test_query.py @@ -865,6 +865,40 @@ def make_entities(): assert not more +@pytest.mark.usefixtures("client_context") +def test_fetch_page_in_query(dispose_of): + page_size = 5 + n_entities = page_size * 2 + + class SomeKind(ndb.Model): + foo = ndb.IntegerProperty() + + @ndb.toplevel + def make_entities(): + entities = [SomeKind(foo=n_entities) for i in range(n_entities)] + keys = yield [entity.put_async() for entity in entities] + raise ndb.Return(keys) + + for key in make_entities(): + dispose_of(key._key) + + query = SomeKind.query().filter(SomeKind.foo.IN([1, 2, n_entities], server_op=True)) + eventually(query.fetch, length_equals(n_entities)) + + results, cursor, more = query.fetch_page(page_size) + assert len(results) == page_size + assert more + + safe_cursor = cursor.urlsafe() + next_cursor = ndb.Cursor(urlsafe=safe_cursor) + results, cursor, more = query.fetch_page(page_size, start_cursor=next_cursor) + assert len(results) == page_size + + results, cursor, more = query.fetch_page(page_size, start_cursor=cursor) + assert not results + assert not more + + @pytest.mark.usefixtures("client_context") def test_polymodel_query(ds_entity): class Animal(ndb.PolyModel): diff --git a/tests/unit/test_model.py b/tests/unit/test_model.py index 3250d22d..82e4324c 100644 --- a/tests/unit/test_model.py +++ b/tests/unit/test_model.py @@ -549,7 +549,7 @@ def test__IN_wrong_container(): assert model.Property._FIND_METHODS_CACHE == {} @staticmethod - def test__IN(): + def test__IN_default(): prop = model.Property("name", indexed=True) or_node = prop._IN(["a", None, "xy"]) expected = query_module.DisjunctionNode( @@ -561,6 +561,33 @@ def test__IN(): # Also verify the alias assert or_node == prop.IN(["a", None, "xy"]) + @staticmethod + def test__IN_client(): + prop = model.Property("name", indexed=True) + or_node = prop._IN(["a", None, "xy"], server_op=False) + expected = query_module.DisjunctionNode( + query_module.FilterNode("name", "=", "a"), + query_module.FilterNode("name", "=", None), + query_module.FilterNode("name", "=", "xy"), + ) + assert or_node == expected + # Also verify the alias + assert or_node == prop.IN(["a", None, "xy"]) + + @staticmethod + def test_server__IN(): + prop = model.Property("name", indexed=True) + in_node = prop._IN(["a", None, "xy"], server_op=True) + assert in_node == prop.IN(["a", None, "xy"], server_op=True) + assert in_node != query_module.DisjunctionNode( + query_module.FilterNode("name", "=", "a"), + query_module.FilterNode("name", "=", None), + query_module.FilterNode("name", "=", "xy"), + ) + assert in_node == query_module.FilterNode( + "name", "in", ["a", None, "xy"], server_op=True + ) + @staticmethod def test___neg__(): prop = model.Property("name") diff --git a/tests/unit/test_query.py b/tests/unit/test_query.py index 589c9bcc..13da4740 100644 --- a/tests/unit/test_query.py +++ b/tests/unit/test_query.py @@ -701,13 +701,6 @@ def test__to_ne_filter_op(): filter_node = query_module.FilterNode("speed", "!=", 88) assert filter_node._to_filter(post=True) is None - @staticmethod - def test__to_filter_bad_op(): - filter_node = query_module.FilterNode("speed", ">=", 88) - filter_node._opsymbol = "in" - with pytest.raises(NotImplementedError): - filter_node._to_filter() - @staticmethod @mock.patch("google.cloud.ndb._datastore_query") def test__to_filter(_datastore_query): From 7dfd56b48380f62061c51a43f0c1ee402107a68b Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Wed, 28 Feb 2024 09:46:28 -0800 Subject: [PATCH 17/21] build(deps): bump cryptography from 42.0.2 to 42.0.4 in .kokoro (#953) Source-Link: https://github.com/googleapis/synthtool/commit/d895aec3679ad22aa120481f746bf9f2f325f26f Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:98f3afd11308259de6e828e37376d18867fd321aba07826e29e4f8d9cab56bad Co-authored-by: Owl Bot Co-authored-by: Jim Morrison --- .github/.OwlBot.lock.yaml | 4 +-- .kokoro/requirements.txt | 66 +++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 51213ca0..e4e943e0 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,5 +13,5 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:a0c4463fcfd9893fc172a3b3db2b6ac0c7b94ec6ad458c7dcea12d9693615ac3 -# created: 2024-02-17T12:21:23.177926195Z + digest: sha256:98f3afd11308259de6e828e37376d18867fd321aba07826e29e4f8d9cab56bad +# created: 2024-02-27T15:56:18.442440378Z diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index f80bdcd6..bda8e38c 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -93,39 +93,39 @@ colorlog==6.7.0 \ # via # gcp-docuploader # nox -cryptography==42.0.2 \ - --hash=sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380 \ - --hash=sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589 \ - --hash=sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea \ - --hash=sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65 \ - --hash=sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a \ - --hash=sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3 \ - --hash=sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008 \ - --hash=sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1 \ - --hash=sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2 \ - --hash=sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635 \ - --hash=sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2 \ - --hash=sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90 \ - --hash=sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee \ - --hash=sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a \ - --hash=sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242 \ - --hash=sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12 \ - --hash=sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2 \ - --hash=sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d \ - --hash=sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be \ - --hash=sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee \ - --hash=sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6 \ - --hash=sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529 \ - --hash=sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929 \ - --hash=sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1 \ - --hash=sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6 \ - --hash=sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a \ - --hash=sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446 \ - --hash=sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9 \ - --hash=sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888 \ - --hash=sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4 \ - --hash=sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33 \ - --hash=sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f +cryptography==42.0.4 \ + --hash=sha256:01911714117642a3f1792c7f376db572aadadbafcd8d75bb527166009c9f1d1b \ + --hash=sha256:0e89f7b84f421c56e7ff69f11c441ebda73b8a8e6488d322ef71746224c20fce \ + --hash=sha256:12d341bd42cdb7d4937b0cabbdf2a94f949413ac4504904d0cdbdce4a22cbf88 \ + --hash=sha256:15a1fb843c48b4a604663fa30af60818cd28f895572386e5f9b8a665874c26e7 \ + --hash=sha256:1cdcdbd117681c88d717437ada72bdd5be9de117f96e3f4d50dab3f59fd9ab20 \ + --hash=sha256:1df6fcbf60560d2113b5ed90f072dc0b108d64750d4cbd46a21ec882c7aefce9 \ + --hash=sha256:3c6048f217533d89f2f8f4f0fe3044bf0b2090453b7b73d0b77db47b80af8dff \ + --hash=sha256:3e970a2119507d0b104f0a8e281521ad28fc26f2820687b3436b8c9a5fcf20d1 \ + --hash=sha256:44a64043f743485925d3bcac548d05df0f9bb445c5fcca6681889c7c3ab12764 \ + --hash=sha256:4e36685cb634af55e0677d435d425043967ac2f3790ec652b2b88ad03b85c27b \ + --hash=sha256:5f8907fcf57392cd917892ae83708761c6ff3c37a8e835d7246ff0ad251d9298 \ + --hash=sha256:69b22ab6506a3fe483d67d1ed878e1602bdd5912a134e6202c1ec672233241c1 \ + --hash=sha256:6bfadd884e7280df24d26f2186e4e07556a05d37393b0f220a840b083dc6a824 \ + --hash=sha256:6d0fbe73728c44ca3a241eff9aefe6496ab2656d6e7a4ea2459865f2e8613257 \ + --hash=sha256:6ffb03d419edcab93b4b19c22ee80c007fb2d708429cecebf1dd3258956a563a \ + --hash=sha256:810bcf151caefc03e51a3d61e53335cd5c7316c0a105cc695f0959f2c638b129 \ + --hash=sha256:831a4b37accef30cccd34fcb916a5d7b5be3cbbe27268a02832c3e450aea39cb \ + --hash=sha256:887623fe0d70f48ab3f5e4dbf234986b1329a64c066d719432d0698522749929 \ + --hash=sha256:a0298bdc6e98ca21382afe914c642620370ce0470a01e1bef6dd9b5354c36854 \ + --hash=sha256:a1327f280c824ff7885bdeef8578f74690e9079267c1c8bd7dc5cc5aa065ae52 \ + --hash=sha256:c1f25b252d2c87088abc8bbc4f1ecbf7c919e05508a7e8628e6875c40bc70923 \ + --hash=sha256:c3a5cbc620e1e17009f30dd34cb0d85c987afd21c41a74352d1719be33380885 \ + --hash=sha256:ce8613beaffc7c14f091497346ef117c1798c202b01153a8cc7b8e2ebaaf41c0 \ + --hash=sha256:d2a27aca5597c8a71abbe10209184e1a8e91c1fd470b5070a2ea60cafec35bcd \ + --hash=sha256:dad9c385ba8ee025bb0d856714f71d7840020fe176ae0229de618f14dae7a6e2 \ + --hash=sha256:db4b65b02f59035037fde0998974d84244a64c3265bdef32a827ab9b63d61b18 \ + --hash=sha256:e09469a2cec88fb7b078e16d4adec594414397e8879a4341c6ace96013463d5b \ + --hash=sha256:e53dc41cda40b248ebc40b83b31516487f7db95ab8ceac1f042626bc43a2f992 \ + --hash=sha256:f1e85a178384bf19e36779d91ff35c7617c885da487d689b05c1366f9933ad74 \ + --hash=sha256:f47be41843200f7faec0683ad751e5ef11b9a56a220d57f300376cd8aba81660 \ + --hash=sha256:fb0cef872d8193e487fc6bdb08559c3aa41b659a7d9be48b2e10747f47863925 \ + --hash=sha256:ffc73996c4fca3d2b6c1c8c12bfd3ad00def8621da24f547626bf06441400449 # via # gcp-releasetool # secretstorage From 17caf0b5f7d0c4d18522f676c8af990b8ff8462d Mon Sep 17 00:00:00 2001 From: Pedro Antonio Date: Wed, 28 Feb 2024 23:12:02 -0300 Subject: [PATCH 18/21] feat: Add field information when raising validation errors. (#956) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Add field information when raising validation errors. * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix: Fix string formatting error --------- Co-authored-by: Owl Bot --- google/cloud/ndb/model.py | 72 ++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/google/cloud/ndb/model.py b/google/cloud/ndb/model.py index b43d4163..3ede1952 100644 --- a/google/cloud/ndb/model.py +++ b/google/cloud/ndb/model.py @@ -1485,7 +1485,9 @@ def _set_value(self, entity, value): if self._repeated: if not isinstance(value, (list, tuple, set, frozenset)): raise exceptions.BadValueError( - "Expected list or tuple, got {!r}".format(value) + "In field {}, expected list or tuple, got {!r}".format( + self._name, value + ) ) value = [self._do_validate(v) for v in value] else: @@ -2372,7 +2374,9 @@ def _validate(self, value): .BadValueError: If ``value`` is not a :class:`bool`. """ if not isinstance(value, bool): - raise exceptions.BadValueError("Expected bool, got {!r}".format(value)) + raise exceptions.BadValueError( + "In field {}, expected bool, got {!r}".format(self._name, value) + ) return value def _from_base_type(self, value): @@ -2417,7 +2421,9 @@ def _validate(self, value): to one. """ if not isinstance(value, six.integer_types): - raise exceptions.BadValueError("Expected integer, got {!r}".format(value)) + raise exceptions.BadValueError( + "In field {}, expected integer, got {!r}".format(self._name, value) + ) return int(value) @@ -2447,7 +2453,9 @@ def _validate(self, value): to one. """ if not isinstance(value, six.integer_types + (float,)): - raise exceptions.BadValueError("Expected float, got {!r}".format(value)) + raise exceptions.BadValueError( + "In field {}, expected float, got {!r}".format(self._name, value) + ) return float(value) @@ -2578,7 +2586,9 @@ def _validate(self, value): exceeds the maximum length (1500 bytes). """ if not isinstance(value, bytes): - raise exceptions.BadValueError("Expected bytes, got {!r}".format(value)) + raise exceptions.BadValueError( + "In field {}, expected bytes, got {!r}".format(self._name, value) + ) if self._indexed and len(value) > _MAX_STRING_LENGTH: raise exceptions.BadValueError( @@ -2761,11 +2771,13 @@ def _validate(self, value): value = value.decode("utf-8") except UnicodeError: raise exceptions.BadValueError( - "Expected valid UTF-8, got {!r}".format(value) + "In field {}, expected valid UTF-8, got {!r}".format( + self._name, value + ) ) else: raise exceptions.BadValueError( - "Expected string, got {!r}".format(value) + "In field {}, expected string, got {!r}".format(self._name, value) ) def _to_base_type(self, value): @@ -2920,7 +2932,9 @@ def _validate(self, value): value = value.decode("utf-8") except UnicodeError: raise exceptions.BadValueError( - "Expected valid UTF-8, got {!r}".format(value) + "In field {}, expected valid UTF-8, got {!r}".format( + self._name, value + ) ) elif isinstance(value, six.string_types): encoded_length = len(value.encode("utf-8")) @@ -3026,7 +3040,9 @@ def _validate(self, value): .BadValueError: If ``value`` is not a :attr:`.GeoPt`. """ if not isinstance(value, GeoPt): - raise exceptions.BadValueError("Expected GeoPt, got {!r}".format(value)) + raise exceptions.BadValueError( + "In field {}, expected GeoPt, got {!r}".format(self._name, value) + ) class PickleProperty(BlobProperty): @@ -3447,7 +3463,9 @@ def _validate(self, value): """ # Might be GAE User or our own version if type(value).__name__ != "User": - raise exceptions.BadValueError("Expected User, got {!r}".format(value)) + raise exceptions.BadValueError( + "In field {}, expected User, got {!r}".format(self._name, value) + ) def _prepare_for_put(self, entity): """Pre-put hook @@ -3659,19 +3677,22 @@ def _validate(self, value): and ``value`` does not match that kind. """ if not isinstance(value, Key): - raise exceptions.BadValueError("Expected Key, got {!r}".format(value)) + raise exceptions.BadValueError( + "In field {}, expected Key, got {!r}".format(self._name, value) + ) # Reject incomplete keys. if not value.id(): raise exceptions.BadValueError( - "Expected complete Key, got {!r}".format(value) + "In field {}, expected complete Key, got {!r}".format(self._name, value) ) # Verify kind if provided. if self._kind is not None: if value.kind() != self._kind: raise exceptions.BadValueError( - "Expected Key with kind={!r}, got " "{!r}".format(self._kind, value) + "In field {}, expected Key with kind={!r}, got " + "{!r}".format(self._name, self._kind, value) ) def _to_base_type(self, value): @@ -3722,7 +3743,9 @@ def _validate(self, value): :class:`~google.cloud.ndb.model.BlobKey`. """ if not isinstance(value, BlobKey): - raise exceptions.BadValueError("Expected BlobKey, got {!r}".format(value)) + raise exceptions.BadValueError( + "In field {}, expected BlobKey, got {!r}".format(self._name, value) + ) class DateTimeProperty(Property): @@ -3838,7 +3861,9 @@ def _validate(self, value): .BadValueError: If ``value`` is not a :class:`~datetime.datetime`. """ if not isinstance(value, datetime.datetime): - raise exceptions.BadValueError("Expected datetime, got {!r}".format(value)) + raise exceptions.BadValueError( + "In field {}, expected datetime, got {!r}".format(self._name, value) + ) if self._tzinfo is None and value.tzinfo is not None: raise exceptions.BadValueError( @@ -3935,7 +3960,9 @@ def _validate(self, value): .BadValueError: If ``value`` is not a :class:`~datetime.date`. """ if not isinstance(value, datetime.date): - raise exceptions.BadValueError("Expected date, got {!r}".format(value)) + raise exceptions.BadValueError( + "In field {}, expected date, got {!r}".format(self._name, value) + ) def _to_base_type(self, value): """Convert a value to the "base" value type for this property. @@ -3993,7 +4020,9 @@ def _validate(self, value): .BadValueError: If ``value`` is not a :class:`~datetime.time`. """ if not isinstance(value, datetime.time): - raise exceptions.BadValueError("Expected time, got {!r}".format(value)) + raise exceptions.BadValueError( + "In field {}, expected time, got {!r}".format(self._name, value) + ) def _to_base_type(self, value): """Convert a value to the "base" value type for this property. @@ -4191,8 +4220,9 @@ def _validate(self, value): return self._model_class(**value) if not isinstance(value, self._model_class): raise exceptions.BadValueError( - "Expected %s instance, got %s" - % (self._model_class.__name__, value.__class__) + "In field {}, expected {} instance, got {!r}".format( + self._name, self._model_class.__name__, value.__class__ + ) ) def _has_value(self, entity, rest=None): @@ -4399,7 +4429,9 @@ def _validate(self, value): if not isinstance(value, self._model_class): raise exceptions.BadValueError( - "Expected {}, got {!r}".format(self._model_class.__name__, value) + "In field {}, expected {}, got {!r}".format( + self._name, self._model_class.__name__, value + ) ) def _get_for_dict(self, entity): From 455f860343ff1b71232dad98cf91415492a899ca Mon Sep 17 00:00:00 2001 From: ventice11o <159263040+ventice11o@users.noreply.github.com> Date: Thu, 29 Feb 2024 20:16:14 +0400 Subject: [PATCH 19/21] fix: repeated structured property containing blob property with legacy_data (#817) (#946) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix issue 817 * Fix issue 817 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: venticello Co-authored-by: Owl Bot Co-authored-by: Jim Morrison --- google/cloud/ndb/model.py | 13 +++++++---- tests/unit/test_model.py | 46 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/google/cloud/ndb/model.py b/google/cloud/ndb/model.py index 3ede1952..51d082f7 100644 --- a/google/cloud/ndb/model.py +++ b/google/cloud/ndb/model.py @@ -2662,11 +2662,16 @@ def _to_datastore(self, entity, data, prefix="", repeated=False): value = compressed_value data[key] = value if not self._repeated: - if value and not value.startswith(_ZLIB_COMPRESSION_MARKERS): - value = zlib.compress(value) - data[key] = value + values = [ + zlib.compress(v) + if v and not v.startswith(_ZLIB_COMPRESSION_MARKERS) + else v + for v in (value if repeated else [value]) + ] + value = values if repeated else values[0] + data[key] = value - if value: + if value and not repeated: data.setdefault("_meanings", {})[key] = ( _MEANING_COMPRESSED, value, diff --git a/tests/unit/test_model.py b/tests/unit/test_model.py index 82e4324c..b57a6040 100644 --- a/tests/unit/test_model.py +++ b/tests/unit/test_model.py @@ -1816,6 +1816,52 @@ class ThisKind(model.Model): compressed_value_two, ] + @staticmethod + def test__to_datastore_legacy_compressed_repeated_in_parent(in_context): + class ThisKind(model.Model): + bar = model.BlobProperty(compressed=True, repeated=False) + + class ParentKind(model.Model): + foo = model.StructuredProperty(ThisKind, repeated=True) + + with in_context.new(legacy_data=True).use(): + uncompressed_value_one = b"abc" * 1000 + compressed_value_one = zlib.compress(uncompressed_value_one) + uncompressed_value_two = b"xyz" * 1000 + compressed_value_two = zlib.compress(uncompressed_value_two) + entity = ParentKind( + foo=[ + ThisKind(bar=uncompressed_value_one), + ThisKind(bar=uncompressed_value_two), + ] + ) + ds_entity = model._entity_to_ds_entity(entity) + assert "foo.bar" not in ds_entity._meanings + assert "foo.bar" in ds_entity.keys() + assert ds_entity.get("foo.bar") == [ + compressed_value_one, + compressed_value_two, + ] + + @staticmethod + def test__to_datastore_legacy_compressed_repeated_in_parent_uninitialized( + in_context, + ): + class ThisKind(model.Model): + bar = model.BlobProperty(compressed=True, repeated=False) + + class ParentKind(model.Model): + foo = model.StructuredProperty(ThisKind, repeated=True) + + with in_context.new(legacy_data=True).use(): + uncompressed_value = b"abc" * 1000 + compressed_value = zlib.compress(uncompressed_value) + entity = ParentKind(foo=[ThisKind(), ThisKind(bar=uncompressed_value)]) + ds_entity = model._entity_to_ds_entity(entity) + assert "foo.bar" not in ds_entity._meanings + assert "foo.bar" in ds_entity.keys() + assert ds_entity.get("foo.bar") == [None, compressed_value] + @staticmethod @pytest.mark.usefixtures("in_context") def test__to_datastore_compressed_uninitialized(): From f0b0724d7e364cc3f3574e77076465657089b09c Mon Sep 17 00:00:00 2001 From: Jim Morrison Date: Thu, 29 Feb 2024 20:13:07 -0800 Subject: [PATCH 20/21] feat: Add support for server side NOT_IN filter. (#957) * tests: Add a test for IN queries with a more complex python object. * feat: Add support for server side NOT_IN filter. * Use NOT IN for GQL instead of NOT_IN. * Add missing test for GQL parameter resolving. --- google/cloud/ndb/_datastore_query.py | 1 + google/cloud/ndb/_gql.py | 26 +++++++++----- google/cloud/ndb/model.py | 51 ++++++++++++++++++---------- google/cloud/ndb/query.py | 5 ++- tests/system/test_query.py | 49 ++++++++++++++++++++++++++ tests/unit/test__gql.py | 35 +++++++++++++++++++ tests/unit/test_model.py | 11 +++++- tests/unit/test_query.py | 14 ++++++++ 8 files changed, 163 insertions(+), 29 deletions(-) diff --git a/google/cloud/ndb/_datastore_query.py b/google/cloud/ndb/_datastore_query.py index 480a2a68..7dd98a4c 100644 --- a/google/cloud/ndb/_datastore_query.py +++ b/google/cloud/ndb/_datastore_query.py @@ -58,6 +58,7 @@ ">=": query_pb2.PropertyFilter.Operator.GREATER_THAN_OR_EQUAL, "!=": query_pb2.PropertyFilter.Operator.NOT_EQUAL, "in": query_pb2.PropertyFilter.Operator.IN, + "not_in": query_pb2.PropertyFilter.Operator.NOT_IN, } _KEY_NOT_IN_CACHE = object() diff --git a/google/cloud/ndb/_gql.py b/google/cloud/ndb/_gql.py index 2d0a2745..60a17075 100644 --- a/google/cloud/ndb/_gql.py +++ b/google/cloud/ndb/_gql.py @@ -30,9 +30,9 @@ class GQL(object): [OFFSET ] [HINT (ORDER_FIRST | FILTER_FIRST | ANCESTOR_FIRST)] [;] - := {< | <= | > | >= | = | != | IN} - := {< | <= | > | >= | = | != | IN} CAST() - := IN (, ...) + := {< | <= | > | >= | = | != | IN | NOT IN} + := {< | <= | > | >= | = | != | IN | NOT IN} CAST() + := {IN | NOT IN} (, ...) := ANCESTOR IS The class is implemented using some basic regular expression tokenization @@ -186,7 +186,7 @@ def _entity(self): _identifier_regex = re.compile(r"(\w+(?:\.\w+)*)$") _quoted_identifier_regex = re.compile(r'((?:"[^"\s]+")+)$') - _conditions_regex = re.compile(r"(<=|>=|!=|=|<|>|is|in)$", re.IGNORECASE) + _conditions_regex = re.compile(r"(<=|>=|!=|=|<|>|is|in|not)$", re.IGNORECASE) _number_regex = re.compile(r"(\d+)$") _cast_regex = re.compile(r"(geopt|user|key|date|time|datetime)$", re.IGNORECASE) @@ -325,6 +325,9 @@ def _FilterList(self): condition = self._AcceptRegex(self._conditions_regex) if not condition: self._Error("Invalid WHERE Condition") + if condition.lower() == "not": + condition += "_" + self._AcceptRegex(self._conditions_regex) + self._CheckFilterSyntax(identifier, condition) if not self._AddSimpleFilter(identifier, condition, self._Reference()): @@ -366,22 +369,25 @@ def _GetValueList(self): return params - def _CheckFilterSyntax(self, identifier, condition): + def _CheckFilterSyntax(self, identifier, raw_condition): """Check that filter conditions are valid and throw errors if not. Args: identifier (str): identifier being used in comparison. condition (str): comparison operator used in the filter. """ + condition = raw_condition.lower() if identifier.lower() == "ancestor": - if condition.lower() == "is": + if condition == "is": if self._has_ancestor: self._Error('Only one ANCESTOR IS" clause allowed') else: self._Error('"IS" expected to follow "ANCESTOR"') - elif condition.lower() == "is": + elif condition == "is": self._Error('"IS" can only be used when comparing against "ANCESTOR"') + elif condition.startswith("not") and condition != "not_in": + self._Error('"NOT " can only be used as "NOT IN"') def _AddProcessedParameterFilter(self, identifier, condition, operator, parameters): """Add a filter with post-processing required. @@ -409,8 +415,8 @@ def _AddProcessedParameterFilter(self, identifier, condition, operator, paramete filter_rule = (self._ANCESTOR, "is") assert condition.lower() == "is" - if operator == "list" and condition.lower() != "in": - self._Error("Only IN can process a list of values") + if operator == "list" and condition.lower() not in ["in", "not_in"]: + self._Error("Only IN can process a list of values, given '%s'" % condition) self._filters.setdefault(filter_rule, []).append((operator, parameters)) return True @@ -676,6 +682,8 @@ def query_filters(self, model_class, filters): node = query_module.ParameterNode(prop, op, val) elif op == "in": node = prop._IN(val) + elif op == "not_in": + node = prop._NOT_IN(val) else: node = prop._comparison(op, val) filters.append(node) diff --git a/google/cloud/ndb/model.py b/google/cloud/ndb/model.py index 51d082f7..994daa42 100644 --- a/google/cloud/ndb/model.py +++ b/google/cloud/ndb/model.py @@ -1258,6 +1258,36 @@ def __ge__(self, value): """FilterNode: Represents the ``>=`` comparison.""" return self._comparison(">=", value) + def _validate_and_canonicalize_values(self, value): + if not self._indexed: + raise exceptions.BadFilterError( + "Cannot query for unindexed property {}".format(self._name) + ) + + if not isinstance(value, (list, tuple, set, frozenset)): + raise exceptions.BadArgumentError( + "For field {}, expected list, tuple or set, got {!r}".format( + self._name, value + ) + ) + + values = [] + for sub_value in value: + if sub_value is not None: + sub_value = self._do_validate(sub_value) + sub_value = self._call_to_base_type(sub_value) + sub_value = self._datastore_type(sub_value) + values.append(sub_value) + return values + + def _NOT_IN(self, value, server_op=False): + """.FilterNode: Represents the ``not_in`` filter.""" + # Import late to avoid circular imports. + from google.cloud.ndb import query + + values = self._validate_and_canonicalize_values(value) + return query.FilterNode(self._name, "not_in", values) + def _IN(self, value, server_op=False): """For the ``in`` comparison operator. @@ -1297,27 +1327,12 @@ def _IN(self, value, server_op=False): # Import late to avoid circular imports. from google.cloud.ndb import query - if not self._indexed: - raise exceptions.BadFilterError( - "Cannot query for unindexed property {}".format(self._name) - ) - - if not isinstance(value, (list, tuple, set, frozenset)): - raise exceptions.BadArgumentError( - "Expected list, tuple or set, got {!r}".format(value) - ) - - values = [] - for sub_value in value: - if sub_value is not None: - sub_value = self._do_validate(sub_value) - sub_value = self._call_to_base_type(sub_value) - sub_value = self._datastore_type(sub_value) - values.append(sub_value) - + values = self._validate_and_canonicalize_values(value) return query.FilterNode(self._name, "in", values, server_op=server_op) IN = _IN + NOT_IN = _NOT_IN + """Used to check if a property value is contained in a set of values. For example: diff --git a/google/cloud/ndb/query.py b/google/cloud/ndb/query.py index 6109fe11..bc2beadc 100644 --- a/google/cloud/ndb/query.py +++ b/google/cloud/ndb/query.py @@ -172,9 +172,10 @@ def ranked(cls, rank): _EQ_OP = "=" _NE_OP = "!=" _IN_OP = "in" +_NOT_IN_OP = "not_in" _LT_OP = "<" _GT_OP = ">" -_OPS = frozenset([_EQ_OP, _NE_OP, _LT_OP, "<=", _GT_OP, ">=", _IN_OP]) +_OPS = frozenset([_EQ_OP, _NE_OP, _LT_OP, "<=", _GT_OP, ">=", _IN_OP, _NOT_IN_OP]) _log = logging.getLogger(__name__) @@ -589,6 +590,8 @@ def resolve(self, bindings, used): value = self._param.resolve(bindings, used) if self._op == _IN_OP: return self._prop._IN(value) + elif self._op == _NOT_IN_OP: + return self._prop._NOT_IN(value) else: return self._prop._comparison(self._op, value) diff --git a/tests/system/test_query.py b/tests/system/test_query.py index fb2e9bbb..12bac380 100644 --- a/tests/system/test_query.py +++ b/tests/system/test_query.py @@ -1866,6 +1866,55 @@ class SomeKind(ndb.Model): assert results[1].foo == 3 +@pytest.mark.filterwarnings("ignore") +@pytest.mark.usefixtures("client_context") +def test_IN_timestamp(ds_entity): + for i in range(5): + entity_id = test_utils.system.unique_resource_id() + ds_entity(KIND, entity_id, foo=datetime.datetime.fromtimestamp(i)) + + class SomeKind(ndb.Model): + foo = ndb.DateTimeProperty() + + eventually(SomeKind.query().fetch, length_equals(5)) + + t2 = datetime.datetime.fromtimestamp(2) + t3 = datetime.datetime.fromtimestamp(3) + + query = SomeKind.query(SomeKind.foo.IN((t2, t3), server_op=True)) + results = query.fetch() + assert len(results) == 2 + assert results[0].foo == t2 + assert results[1].foo == t3 + + +@pytest.mark.filterwarnings("ignore") +@pytest.mark.usefixtures("client_context") +def test_NOT_IN(ds_entity): + for i in range(5): + entity_id = test_utils.system.unique_resource_id() + ds_entity(KIND, entity_id, foo=i, pt=ndb.GeoPt(i, i)) + + class SomeKind(ndb.Model): + foo = ndb.IntegerProperty() + pt = ndb.GeoPtProperty() + + eventually(SomeKind.query().fetch, length_equals(5)) + + query = SomeKind.query(SomeKind.pt.NOT_IN([ndb.GeoPt(1, 1)])) + results = query.fetch() + assert len(results) == 4 + assert results[0].foo == 0 + assert results[1].foo == 2 + + query = SomeKind.gql("where foo not in :1", [2, 3]) + results = query.fetch() + assert len(results) == 3 + assert results[0].foo == 0 + assert results[1].foo == 1 + assert results[2].foo == 4 + + @pytest.mark.usefixtures("client_context") def test_projection_with_json_property(dispose_of): """Regression test for #378 diff --git a/tests/unit/test__gql.py b/tests/unit/test__gql.py index ee9371c8..3c96d4fe 100644 --- a/tests/unit/test__gql.py +++ b/tests/unit/test__gql.py @@ -198,11 +198,24 @@ def test_in_list(): ("prop1", "IN"): [("list", [Literal(1), Literal(2), Literal(3)])] } + @staticmethod + def test_not_in_list(): + Literal = gql_module.Literal + gql = gql_module.GQL("SELECT * FROM SomeKind WHERE prop1 NOT IN (1, 2, 3)") + assert gql.filters() == { + ("prop1", "NOT_IN"): [("list", [Literal(1), Literal(2), Literal(3)])] + } + @staticmethod def test_cast_list_no_in(): with pytest.raises(exceptions.BadQueryError): gql_module.GQL("SELECT * FROM SomeKind WHERE prop1=(1, 2, 3)") + @staticmethod + def test_not_without_in(): + with pytest.raises(exceptions.BadQueryError): + gql_module.GQL("SELECT * FROM SomeKind WHERE prop1 NOT=1") + @staticmethod def test_reference(): gql = gql_module.GQL("SELECT * FROM SomeKind WHERE prop1=:ref") @@ -328,6 +341,16 @@ class SomeKind(model.Model): query_module.FilterNode("prop1", "=", 3), ) + @staticmethod + @pytest.mark.usefixtures("in_context") + def test_get_query_not_in(): + class SomeKind(model.Model): + prop1 = model.IntegerProperty() + + gql = gql_module.GQL("SELECT prop1 FROM SomeKind WHERE prop1 NOT IN (1, 2)") + query = gql.get_query() + assert query.filters == query_module.FilterNode("prop1", "not_in", [1, 2]) + @staticmethod @pytest.mark.usefixtures("in_context") def test_get_query_in_parameterized(): @@ -338,6 +361,18 @@ class SomeKind(model.Model): query = gql.get_query() assert "'in'," in str(query.filters) + @staticmethod + @pytest.mark.usefixtures("in_context") + def test_get_query_not_in_parameterized(): + class SomeKind(model.Model): + prop1 = model.StringProperty() + + gql = gql_module.GQL( + "SELECT prop1 FROM SomeKind WHERE prop1 NOT IN (:1, :2, :3)" + ) + query = gql.get_query() + assert "'not_in'," in str(query.filters) + @staticmethod @pytest.mark.usefixtures("in_context") def test_get_query_keys_only(): diff --git a/tests/unit/test_model.py b/tests/unit/test_model.py index b57a6040..14f03cef 100644 --- a/tests/unit/test_model.py +++ b/tests/unit/test_model.py @@ -575,7 +575,7 @@ def test__IN_client(): assert or_node == prop.IN(["a", None, "xy"]) @staticmethod - def test_server__IN(): + def test__IN_server(): prop = model.Property("name", indexed=True) in_node = prop._IN(["a", None, "xy"], server_op=True) assert in_node == prop.IN(["a", None, "xy"], server_op=True) @@ -588,6 +588,15 @@ def test_server__IN(): "name", "in", ["a", None, "xy"], server_op=True ) + @staticmethod + def test__NOT_IN(): + prop = model.Property("name", indexed=True) + not_in_node = prop._NOT_IN(["a", None, "xy"]) + assert not_in_node == prop.NOT_IN(["a", None, "xy"]) + assert not_in_node == query_module.FilterNode( + "name", "not_in", ["a", None, "xy"] + ) + @staticmethod def test___neg__(): prop = model.Property("name") diff --git a/tests/unit/test_query.py b/tests/unit/test_query.py index 13da4740..33b560b4 100644 --- a/tests/unit/test_query.py +++ b/tests/unit/test_query.py @@ -2408,3 +2408,17 @@ class SomeKind(model.Model): query = query_module.gql(gql_query, *positional, **keywords) compat_rep = "'xxx'" assert query.__repr__() == rep.format(compat_rep) + + @staticmethod + @pytest.mark.usefixtures("in_context") + def test_gql_with_bind_not_in(): + class SomeKind(model.Model): + prop1 = model.StringProperty() + + query = query_module.gql( + "SELECT * FROM SomeKind WHERE prop1 not in :1", ["a", "b", "c"] + ) + assert ( + query.__repr__() + == "Query(kind='SomeKind', filters=FilterNode('prop1', 'not_in', ['a', 'b', 'c']), order_by=[], offset=0)" + ) From 70ebac149c04400221506a38ef9aeae909364371 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Thu, 29 Feb 2024 20:57:09 -0800 Subject: [PATCH 21/21] chore(main): release 2.3.0 (#920) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- CHANGELOG.md | 27 +++++++++++++++++++++++++++ google/cloud/ndb/version.py | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df871908..c1588791 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,33 @@ [1]: https://pypi.org/project/google-cloud-ndb/#history +## [2.3.0](https://github.com/googleapis/python-ndb/compare/v2.2.2...v2.3.0) (2024-03-01) + + +### Features + +* Add field information when raising validation errors. ([#956](https://github.com/googleapis/python-ndb/issues/956)) ([17caf0b](https://github.com/googleapis/python-ndb/commit/17caf0b5f7d0c4d18522f676c8af990b8ff8462d)) +* Add Python 3.12 ([#949](https://github.com/googleapis/python-ndb/issues/949)) ([b5c8477](https://github.com/googleapis/python-ndb/commit/b5c847783b80071c2dd9e9a3dbf899230c99e64a)) +* Add support for google.cloud.ndb.__version__ ([#929](https://github.com/googleapis/python-ndb/issues/929)) ([42b3f01](https://github.com/googleapis/python-ndb/commit/42b3f0137caed25ac3242435b571155d2d84c78e)) +* Add support for server side NOT_IN filter. ([#957](https://github.com/googleapis/python-ndb/issues/957)) ([f0b0724](https://github.com/googleapis/python-ndb/commit/f0b0724d7e364cc3f3574e77076465657089b09c)) +* Allow queries using server side IN. ([#954](https://github.com/googleapis/python-ndb/issues/954)) ([2646cef](https://github.com/googleapis/python-ndb/commit/2646cef3e2687461174a11c45f29de7b84d1fcdb)) +* Introduce compatibility with native namespace packages ([#933](https://github.com/googleapis/python-ndb/issues/933)) ([ccae387](https://github.com/googleapis/python-ndb/commit/ccae387720a28db2686e69dfe23a2599fc4908f0)) +* Use server side != for queries. ([#950](https://github.com/googleapis/python-ndb/issues/950)) ([106772f](https://github.com/googleapis/python-ndb/commit/106772f031f6c37500a0d463698e59008f9bf19a)) + + +### Bug Fixes + +* Compressed repeated to uncompressed property ([#772](https://github.com/googleapis/python-ndb/issues/772)) ([dab9edf](https://github.com/googleapis/python-ndb/commit/dab9edf0fc161051eb13c296cbe973b3a16b502d)) +* Repeated structured property containing blob property with legacy_data ([#817](https://github.com/googleapis/python-ndb/issues/817)) ([#946](https://github.com/googleapis/python-ndb/issues/946)) ([455f860](https://github.com/googleapis/python-ndb/commit/455f860343ff1b71232dad98cf91415492a899ca)) + + +### Documentation + +* **__init__:** Note that Firestore in Datastore Mode is supported ([#919](https://github.com/googleapis/python-ndb/issues/919)) ([0fa75e7](https://github.com/googleapis/python-ndb/commit/0fa75e71dfc6d56d2c0eaf214a48774b99bb959f)) +* Correct read_consistency docs. ([#948](https://github.com/googleapis/python-ndb/issues/948)) ([7e8481d](https://github.com/googleapis/python-ndb/commit/7e8481db84a6d0b96cf09c38e90f47d6b7847a0b)) +* Fix a mistaken ID description ([#943](https://github.com/googleapis/python-ndb/issues/943)) ([5103813](https://github.com/googleapis/python-ndb/commit/51038139e45807b3a14346ded702fbe202dcfdf2)) +* Show how to use named databases ([#932](https://github.com/googleapis/python-ndb/issues/932)) ([182fe4e](https://github.com/googleapis/python-ndb/commit/182fe4e2d295768aaf016f94cb43b6b1e5572ebd)) + ## [2.2.2](https://github.com/googleapis/python-ndb/compare/v2.2.1...v2.2.2) (2023-09-19) diff --git a/google/cloud/ndb/version.py b/google/cloud/ndb/version.py index 210b9c03..ee9518e1 100644 --- a/google/cloud/ndb/version.py +++ b/google/cloud/ndb/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.2.2" +__version__ = "2.3.0"