From ae72add0d890f9162030f30de7cb31ddfc000cf3 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:33:04 -0500 Subject: [PATCH 001/190] chore: bump urllib3 from 1.26.12 to 1.26.18 (#1022) 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 ec696b558..453b540c1 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:30470597773378105e239b59fce8eb27cc97375580d592699206d17d117143d0 -# created: 2023-11-03T00:57:07.335914631Z + digest: sha256:caffe0a9277daeccc4d1de5c9b55ebba0901b57c2f713ec9c876b0d4ec064f61 +# created: 2023-11-08T19:46:45.022803742Z diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 16170d0ca..8957e2110 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 0432420dcf18304dc1912075482eff0d2dc73009 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Thu, 30 Nov 2023 11:38:28 -0500 Subject: [PATCH 002/190] feat: Introduce compatibility with native namespace packages (#1024) * feat: Introduce compatibility with native namespace packages * use find_namespace_packages in setup.py --- google/__init__.py | 24 ----------------------- google/cloud/__init__.py | 26 ------------------------- noxfile.py | 2 +- owlbot.py | 4 ++-- setup.py | 7 +------ tests/unit/test_packaging.py | 37 ++++++++++++++++++++++++++++++++++++ 6 files changed, 41 insertions(+), 59 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 9a1b64a6d..000000000 --- a/google/__init__.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 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 e1f8a4d20..000000000 --- a/google/cloud/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 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. - -from typing import List - -try: - import pkg_resources - - pkg_resources.declare_namespace(__name__) -except ImportError: - import pkgutil - - __path__: List[str] = pkgutil.extend_path(__path__, __name__) # type: ignore diff --git a/noxfile.py b/noxfile.py index ad452184a..f2cea17e9 100644 --- a/noxfile.py +++ b/noxfile.py @@ -106,7 +106,7 @@ def mypy(session): # TODO: Only check the hand-written layer, the generated code does not pass # mypy checks yet. # https://github.com/googleapis/gapic-generator-python/issues/1092 - session.run("mypy", "google/cloud") + session.run("mypy", "-p", "google.cloud") @nox.session(python=DEFAULT_PYTHON_VERSION) diff --git a/owlbot.py b/owlbot.py index 7539adfdb..451be6e99 100644 --- a/owlbot.py +++ b/owlbot.py @@ -383,7 +383,7 @@ def mypy(session): # TODO: Only check the hand-written layer, the generated code does not pass # mypy checks yet. # https://github.com/googleapis/gapic-generator-python/issues/1092 - session.run("mypy", "google/cloud")''' + session.run("mypy", "-p", "google.cloud")''' ), ) @@ -399,7 +399,7 @@ def mypy(session): ) s.replace( "noxfile.py", - r'session\.run\("mypy", "google/cloud"\)', + r'session\.run\("mypy", "-p", "google.cloud"\)', textwrap.dedent( ''' \g<0> diff --git a/setup.py b/setup.py index 7d70e25bc..72b859321 100644 --- a/setup.py +++ b/setup.py @@ -56,14 +56,10 @@ packages = [ package - for package in setuptools.PEP420PackageFinder.find() + for package in setuptools.find_namespace_packages() if package.startswith("google") ] -namespaces = ["google"] -if "google.cloud" in packages: - namespaces.append("google.cloud") - setuptools.setup( name=name, version=version, @@ -89,7 +85,6 @@ ], platforms="Posix; MacOS X; Windows", packages=packages, - namespace_packages=namespaces, install_requires=dependencies, extras_require=extras, python_requires=">=3.7", diff --git a/tests/unit/test_packaging.py b/tests/unit/test_packaging.py new file mode 100644 index 000000000..6dc70e3d1 --- /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-pubsub``. + 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-pubsub``. + 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 660b8eaf0daf975834a8333aedf8415867a4874d Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 12:56:58 -0500 Subject: [PATCH 003/190] feat: Add support for Python 3.12 (#1025) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(python): Add Python 3.12 Source-Link: https://github.com/googleapis/synthtool/commit/af16e6d4672cc7b400f144de2fc3068b54ff47d2 Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:bacc3af03bff793a03add584537b36b5644342931ad989e3ba1171d3bd5399f5 * Add python 3.12 to setup.py and owlbot.py * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix incorrect usage of assert_called_once() * update required checks --------- Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- .github/.OwlBot.lock.yaml | 4 +- .github/sync-repo-settings.yaml | 12 ++++++ .github/workflows/unittest.yml | 2 +- .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 +++ CONTRIBUTING.rst | 6 ++- noxfile.py | 6 +-- owlbot.py | 5 +-- samples/snippets/noxfile.py | 2 +- setup.py | 1 + .../publisher/test_publisher_client.py | 2 +- 14 files changed, 95 insertions(+), 14 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/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 453b540c1..eb4d9f794 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:caffe0a9277daeccc4d1de5c9b55ebba0901b57c2f713ec9c876b0d4ec064f61 -# created: 2023-11-08T19:46:45.022803742Z + digest: sha256:bacc3af03bff793a03add584537b36b5644342931ad989e3ba1171d3bd5399f5 +# created: 2023-11-23T18:17:28.105124211Z diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml index edda0b570..77c1a4fb5 100644 --- a/.github/sync-repo-settings.yaml +++ b/.github/sync-repo-settings.yaml @@ -15,4 +15,16 @@ branchProtectionRules: - 'Samples - Python 3.8' - 'Samples - Python 3.9' - 'Samples - Python 3.10' + - 'Samples - Python 3.11' + - 'Samples - Python 3.12' - 'OwlBot Post Processor' + - 'docs' + - 'docfx' + - 'lint' + - 'unit (3.7)' + - 'unit (3.8)' + - 'unit (3.9)' + - 'unit (3.10)' + - 'unit (3.11)' + - 'unit (3.12)' + - 'cover' diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index 8057a7691..a32027b49 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: ['3.7', '3.8', '3.9', '3.10', '3.11'] + python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] steps: - name: Checkout uses: actions/checkout@v3 diff --git a/.kokoro/samples/python3.12/common.cfg b/.kokoro/samples/python3.12/common.cfg new file mode 100644 index 000000000..ae6100772 --- /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-pubsub/.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-pubsub/.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 000000000..a1c8d9759 --- /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 000000000..f9cfcd33e --- /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-pubsub/.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 000000000..71cd1e597 --- /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 000000000..a1c8d9759 --- /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/CONTRIBUTING.rst b/CONTRIBUTING.rst index 59283006e..03a700296 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -22,7 +22,7 @@ In order to add a feature: documentation. - 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 @@ -72,7 +72,7 @@ We use `nox `__ to instrument our tests. - To run a single unit test:: - $ nox -s unit-3.11 -- -k + $ nox -s unit-3.12 -- -k .. note:: @@ -226,12 +226,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 f2cea17e9..3f9db8b2c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -36,9 +36,9 @@ DEFAULT_PYTHON_VERSION = "3.8" -UNIT_TEST_PYTHON_VERSIONS: List[str] = ["3.7", "3.8", "3.9", "3.10", "3.11"] +UNIT_TEST_PYTHON_VERSIONS: List[str] = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] UNIT_TEST_STANDARD_DEPENDENCIES = [ - "mock==5.0.0", + "mock", "asyncmock", "pytest", "pytest-cov", @@ -52,7 +52,7 @@ SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.10"] SYSTEM_TEST_STANDARD_DEPENDENCIES: List[str] = [ - "mock==5.0.0", + "mock", "pytest", "google-cloud-testutils", ] diff --git a/owlbot.py b/owlbot.py index 451be6e99..338687588 100644 --- a/owlbot.py +++ b/owlbot.py @@ -336,7 +336,7 @@ samples=True, cov_level=100, versions=gcp.common.detect_versions(path="./google", default_first=True), - unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11"], + unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"], system_test_python_versions=["3.10"], system_test_external_dependencies=["psutil","flaky"], ) @@ -353,9 +353,6 @@ s.replace( "noxfile.py", r'"blacken",', '\g<0>\n "mypy",', ) -s.replace( - "noxfile.py", r'"mock"', '"mock==5.0.0"', -) s.replace( "noxfile.py", r"nox\.options\.error_on_missing_interpreters = True", diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 1224cbe21..3b7135946 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/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/setup.py b/setup.py index 72b859321..d388d9443 100644 --- a/setup.py +++ b/setup.py @@ -80,6 +80,7 @@ "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/tests/unit/pubsub_v1/publisher/test_publisher_client.py b/tests/unit/pubsub_v1/publisher/test_publisher_client.py index 6c68c3943..91c556cd6 100644 --- a/tests/unit/pubsub_v1/publisher/test_publisher_client.py +++ b/tests/unit/pubsub_v1/publisher/test_publisher_client.py @@ -636,7 +636,7 @@ def test_resume_publish(creds): client._set_sequencer(topic=topic, sequencer=sequencer, ordering_key=ordering_key) client.resume_publish(topic, ordering_key) - assert sequencer.unpause.called_once() + sequencer.unpause.assert_called_once() def test_resume_publish_no_sequencer_found(creds): From ad4502850f6cc37c66383d8b2485d2e513a3074b Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:05:41 -0500 Subject: [PATCH 004/190] chore: bump cryptography from 41.0.5 to 41.0.6 in /synthtool/gcp/templates/python_library/.kokoro (#1029) Source-Link: https://github.com/googleapis/synthtool/commit/9367caadcbb30b5b2719f30eb00c44cc913550ed Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:2f155882785883336b4468d5218db737bb1d10c9cea7cb62219ad16fe248c03c Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- .github/.OwlBot.lock.yaml | 4 ++-- .kokoro/requirements.txt | 48 +++++++++++++++++++-------------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index eb4d9f794..773c1dfd2 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:bacc3af03bff793a03add584537b36b5644342931ad989e3ba1171d3bd5399f5 -# created: 2023-11-23T18:17:28.105124211Z + digest: sha256:2f155882785883336b4468d5218db737bb1d10c9cea7cb62219ad16fe248c03c +# created: 2023-11-29T14:54:29.548172703Z diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 8957e2110..e5c1ffca9 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 From 6cfafaa3bd1bc4707008816303cf1e7fc5dee374 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Thu, 30 Nov 2023 20:20:30 +0100 Subject: [PATCH 005/190] chore(deps): update all dependencies (#1016) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(deps): update all dependencies * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot Co-authored-by: Prad Nelluru --- samples/snippets/requirements-test.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index 0d4924032..2dbe36372 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -2,5 +2,5 @@ backoff==2.2.1 pytest==7.4.3 mock==5.1.0 flaky==3.7.0 -google-cloud-bigquery==3.12.0 +google-cloud-bigquery==3.13.0 google-cloud-storage==2.12.0 From 05dd571760b71ae2930072f0677616dfc19d9511 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:47:02 -0500 Subject: [PATCH 006/190] fix: use `retry_async` instead of `retry` in async client (#1030) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: Update gapic-generator-python to v1.12.0 PiperOrigin-RevId: 586356061 Source-Link: https://github.com/googleapis/googleapis/commit/72a1f55abaedbb62decd8ae8a44a4de223799c76 Source-Link: https://github.com/googleapis/googleapis-gen/commit/558a04bcd1cc0576e8fac1089e48e48b27ac161b Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNTU4YTA0YmNkMWNjMDU3NmU4ZmFjMTA4OWU0OGU0OGIyN2FjMTYxYiJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * chore: Update gapic-generator-python to v1.13.0 PiperOrigin-RevId: 586460538 Source-Link: https://github.com/googleapis/googleapis/commit/44582d0577fdc95dd2af37628a0569e16aac0bfe Source-Link: https://github.com/googleapis/googleapis-gen/commit/5e7073c9de847929c4ae97f8a444c3fca2d45a6b Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNWU3MDczYzlkZTg0NzkyOWM0YWU5N2Y4YTQ0NGMzZmNhMmQ0NWE2YiJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- .../services/publisher/async_client.py | 49 ++++++------ .../services/schema_service/async_client.py | 52 ++++++------- .../services/subscriber/async_client.py | 76 +++++++++---------- 3 files changed, 88 insertions(+), 89 deletions(-) diff --git a/google/pubsub_v1/services/publisher/async_client.py b/google/pubsub_v1/services/publisher/async_client.py index 28a247cca..ae632ea96 100644 --- a/google/pubsub_v1/services/publisher/async_client.py +++ b/google/pubsub_v1/services/publisher/async_client.py @@ -33,15 +33,14 @@ from google.api_core.client_options import ClientOptions from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 -from google.api_core import retry as retries -from google.api_core import timeout as timeouts # type: ignore +from google.api_core import retry_async as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore try: - OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault] + OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault] except AttributeError: # pragma: NO COVER - OptionalRetry = Union[retries.Retry, object] # type: ignore + OptionalRetry = Union[retries.AsyncRetry, object] # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -274,7 +273,7 @@ async def sample_create_topic(): This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (TimeoutType): The timeout for this request. @@ -306,7 +305,7 @@ async def sample_create_topic(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.create_topic, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -398,7 +397,7 @@ async def sample_update_topic(): This corresponds to the ``update_mask`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (TimeoutType): The timeout for this request. @@ -432,7 +431,7 @@ async def sample_update_topic(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.update_topic, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -519,7 +518,7 @@ async def sample_publish(): This corresponds to the ``messages`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (TimeoutType): The timeout for this request. @@ -553,7 +552,7 @@ async def sample_publish(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.publish, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=4.0, @@ -636,7 +635,7 @@ async def sample_get_topic(): This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (TimeoutType): The timeout for this request. @@ -668,7 +667,7 @@ async def sample_get_topic(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.get_topic, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -748,7 +747,7 @@ async def sample_list_topics(): This corresponds to the ``project`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (TimeoutType): The timeout for this request. @@ -784,7 +783,7 @@ async def sample_list_topics(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.list_topics, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -875,7 +874,7 @@ async def sample_list_topic_subscriptions(): This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (TimeoutType): The timeout for this request. @@ -911,7 +910,7 @@ async def sample_list_topic_subscriptions(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.list_topic_subscriptions, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1006,7 +1005,7 @@ async def sample_list_topic_snapshots(): This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (TimeoutType): The timeout for this request. @@ -1042,7 +1041,7 @@ async def sample_list_topic_snapshots(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.list_topic_snapshots, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1132,7 +1131,7 @@ async def sample_delete_topic(): This corresponds to the ``topic`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (TimeoutType): The timeout for this request. @@ -1160,7 +1159,7 @@ async def sample_delete_topic(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.delete_topic, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1231,7 +1230,7 @@ async def sample_detach_subscription(): request (Optional[Union[google.pubsub_v1.types.DetachSubscriptionRequest, dict]]): The request object. Request for the DetachSubscription method. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (TimeoutType): The timeout for this request. @@ -1251,7 +1250,7 @@ async def sample_detach_subscription(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.detach_subscription, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1299,7 +1298,7 @@ async def set_iam_policy( request (:class:`~.policy_pb2.SetIamPolicyRequest`): The request object. Request message for `SetIamPolicy` method. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (TimeoutType): The timeout for this request. @@ -1420,7 +1419,7 @@ async def get_iam_policy( request (:class:`~.iam_policy_pb2.GetIamPolicyRequest`): The request object. Request message for `GetIamPolicy` method. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (TimeoutType): The timeout for this request. @@ -1543,7 +1542,7 @@ async def test_iam_permissions( request (:class:`~.iam_policy_pb2.TestIamPermissionsRequest`): The request object. Request message for `TestIamPermissions` method. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (TimeoutType): The timeout for this request. diff --git a/google/pubsub_v1/services/schema_service/async_client.py b/google/pubsub_v1/services/schema_service/async_client.py index 59773b385..2d4b5d77b 100644 --- a/google/pubsub_v1/services/schema_service/async_client.py +++ b/google/pubsub_v1/services/schema_service/async_client.py @@ -33,14 +33,14 @@ from google.api_core.client_options import ClientOptions from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 -from google.api_core import retry as retries +from google.api_core import retry_async as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore try: - OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault] + OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault] except AttributeError: # pragma: NO COVER - OptionalRetry = Union[retries.Retry, object] # type: ignore + OptionalRetry = Union[retries.AsyncRetry, object] # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -291,7 +291,7 @@ async def sample_create_schema(): This corresponds to the ``schema_id`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -326,7 +326,7 @@ async def sample_create_schema(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.create_schema, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -403,7 +403,7 @@ async def sample_get_schema(): This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -434,7 +434,7 @@ async def sample_get_schema(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.get_schema, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -512,7 +512,7 @@ async def sample_list_schemas(): This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -547,7 +547,7 @@ async def sample_list_schemas(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.list_schemas, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -634,7 +634,7 @@ async def sample_list_schema_revisions(): This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -669,7 +669,7 @@ async def sample_list_schema_revisions(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.list_schema_revisions, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -767,7 +767,7 @@ async def sample_commit_schema(): This corresponds to the ``schema`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -800,7 +800,7 @@ async def sample_commit_schema(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.commit_schema, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -890,7 +890,7 @@ async def sample_rollback_schema(): This corresponds to the ``revision_id`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -923,7 +923,7 @@ async def sample_rollback_schema(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.rollback_schema, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1011,7 +1011,7 @@ async def sample_delete_schema_revision(): This corresponds to the ``revision_id`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -1044,7 +1044,7 @@ async def sample_delete_schema_revision(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.delete_schema_revision, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1118,7 +1118,7 @@ async def sample_delete_schema(): This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -1145,7 +1145,7 @@ async def sample_delete_schema(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.delete_schema, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1231,7 +1231,7 @@ async def sample_validate_schema(): This corresponds to the ``schema`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -1266,7 +1266,7 @@ async def sample_validate_schema(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.validate_schema, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1336,7 +1336,7 @@ async def sample_validate_message(): Args: request (Optional[Union[google.pubsub_v1.types.ValidateMessageRequest, dict]]): The request object. Request for the ``ValidateMessage`` method. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -1355,7 +1355,7 @@ async def sample_validate_message(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.validate_message, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1401,7 +1401,7 @@ async def set_iam_policy( request (:class:`~.policy_pb2.SetIamPolicyRequest`): The request object. Request message for `SetIamPolicy` method. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -1521,7 +1521,7 @@ async def get_iam_policy( request (:class:`~.iam_policy_pb2.GetIamPolicyRequest`): The request object. Request message for `GetIamPolicy` method. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -1643,7 +1643,7 @@ async def test_iam_permissions( request (:class:`~.iam_policy_pb2.TestIamPermissionsRequest`): The request object. Request message for `TestIamPermissions` method. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be diff --git a/google/pubsub_v1/services/subscriber/async_client.py b/google/pubsub_v1/services/subscriber/async_client.py index 17b8bc1d2..13f30bd89 100644 --- a/google/pubsub_v1/services/subscriber/async_client.py +++ b/google/pubsub_v1/services/subscriber/async_client.py @@ -37,14 +37,14 @@ from google.api_core.client_options import ClientOptions from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 -from google.api_core import retry as retries +from google.api_core import retry_async as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore try: - OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault] + OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault] except AttributeError: # pragma: NO COVER - OptionalRetry = Union[retries.Retry, object] # type: ignore + OptionalRetry = Union[retries.AsyncRetry, object] # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -344,7 +344,7 @@ async def sample_create_subscription(): This corresponds to the ``ack_deadline_seconds`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -385,7 +385,7 @@ async def sample_create_subscription(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.create_subscription, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -465,7 +465,7 @@ async def sample_get_subscription(): This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -500,7 +500,7 @@ async def sample_get_subscription(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.get_subscription, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -597,7 +597,7 @@ async def sample_update_subscription(): This corresponds to the ``update_mask`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -634,7 +634,7 @@ async def sample_update_subscription(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.update_subscription, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -714,7 +714,7 @@ async def sample_list_subscriptions(): This corresponds to the ``project`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -749,7 +749,7 @@ async def sample_list_subscriptions(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.list_subscriptions, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -840,7 +840,7 @@ async def sample_delete_subscription(): This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -867,7 +867,7 @@ async def sample_delete_subscription(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.delete_subscription, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -971,7 +971,7 @@ async def sample_modify_ack_deadline(): This corresponds to the ``ack_deadline_seconds`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -1002,7 +1002,7 @@ async def sample_modify_ack_deadline(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.modify_ack_deadline, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1093,7 +1093,7 @@ async def sample_acknowledge(): This corresponds to the ``ack_ids`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -1122,7 +1122,7 @@ async def sample_acknowledge(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.acknowledge, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1226,7 +1226,7 @@ async def sample_pull(): This corresponds to the ``max_messages`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -1267,7 +1267,7 @@ async def sample_pull(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.pull, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1363,7 +1363,7 @@ def request_generator(): This request is used to establish the initial stream as well as to stream acknowledgements and ack deadline modifications from the client to the server. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -1380,7 +1380,7 @@ def request_generator(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.streaming_pull, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=4.0, @@ -1472,7 +1472,7 @@ async def sample_modify_push_config(): This corresponds to the ``push_config`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -1501,7 +1501,7 @@ async def sample_modify_push_config(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.modify_push_config, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1582,7 +1582,7 @@ async def sample_get_snapshot(): This corresponds to the ``snapshot`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -1619,7 +1619,7 @@ async def sample_get_snapshot(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.get_snapshot, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1703,7 +1703,7 @@ async def sample_list_snapshots(): This corresponds to the ``project`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -1738,7 +1738,7 @@ async def sample_list_snapshots(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.list_snapshots, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1867,7 +1867,7 @@ async def sample_create_snapshot(): This corresponds to the ``subscription`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -1906,7 +1906,7 @@ async def sample_create_snapshot(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.create_snapshot, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -1996,7 +1996,7 @@ async def sample_update_snapshot(): This corresponds to the ``update_mask`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -2035,7 +2035,7 @@ async def sample_update_snapshot(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.update_snapshot, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -2120,7 +2120,7 @@ async def sample_delete_snapshot(): This corresponds to the ``snapshot`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -2147,7 +2147,7 @@ async def sample_delete_snapshot(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.delete_snapshot, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -2221,7 +2221,7 @@ async def sample_seek(): Args: request (Optional[Union[google.pubsub_v1.types.SeekRequest, dict]]): The request object. Request for the ``Seek`` method. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -2238,7 +2238,7 @@ async def sample_seek(): # and friendly error handling. rpc = gapic_v1.method_async.wrap_method( self._client._transport.seek, - default_retry=retries.Retry( + default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, multiplier=1.3, @@ -2288,7 +2288,7 @@ async def set_iam_policy( request (:class:`~.policy_pb2.SetIamPolicyRequest`): The request object. Request message for `SetIamPolicy` method. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -2408,7 +2408,7 @@ async def get_iam_policy( request (:class:`~.iam_policy_pb2.GetIamPolicyRequest`): The request object. Request message for `GetIamPolicy` method. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be @@ -2530,7 +2530,7 @@ async def test_iam_permissions( request (:class:`~.iam_policy_pb2.TestIamPermissionsRequest`): The request object. Request message for `TestIamPermissions` method. - retry (google.api_core.retry.Retry): Designation of what errors, if any, + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be From 695af43b428795bbe1c817abdce2521fddf33448 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Thu, 30 Nov 2023 22:11:48 +0100 Subject: [PATCH 007/190] chore(deps): update all dependencies (#1032) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(deps): update all dependencies * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * pin protobuf for python 3.7 - See https://github.com/protocolbuffers/protobuf/pull/14148 --------- Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- samples/snippets/requirements-test.txt | 2 +- samples/snippets/requirements.txt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index 2dbe36372..b3325ed9f 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -3,4 +3,4 @@ pytest==7.4.3 mock==5.1.0 flaky==3.7.0 google-cloud-bigquery==3.13.0 -google-cloud-storage==2.12.0 +google-cloud-storage==2.13.0 diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 683de0557..a3124ddce 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,4 +1,5 @@ google-cloud-pubsub==2.18.4 avro==1.11.3 -protobuf==4.24.4 +protobuf===4.24.4; python_version == '3.7' +protobuf==4.25.1; python_version >= '3.8' avro==1.11.3 From ac6d9126413b5c8e2b00727f7d74f03b7fb9d9ed Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Sat, 2 Dec 2023 11:31:49 -0500 Subject: [PATCH 008/190] feat: add `use_table_schema` field to BigQueryConfig (#1035) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add `use_table_schema` field to BigQueryConfig PiperOrigin-RevId: 587079085 Source-Link: https://github.com/googleapis/googleapis/commit/95fabe6ae89c9206e89fd38a4d5c0f40c13bedfb Source-Link: https://github.com/googleapis/googleapis-gen/commit/90b35e9d8bc6780a80db3bababefc29072fa3506 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiOTBiMzVlOWQ4YmM2NzgwYTgwZGIzYmFiYWJlZmMyOTA3MmZhMzUwNiJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot --- google/pubsub_v1/types/pubsub.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index d44778ddc..ff7becd48 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -1111,8 +1111,9 @@ class BigQueryConfig(proto.Message): The name of the table to which to write data, of the form {projectId}.{datasetId}.{tableId} use_topic_schema (bool): - When true, use the topic's schema as the - columns to write to in BigQuery, if it exists. + Optional. When true, use the topic's schema as the columns + to write to in BigQuery, if it exists. ``use_topic_schema`` + and ``use_table_schema`` cannot be enabled at the same time. write_metadata (bool): When true, write the subscription name, message_id, publish_time, attributes, and ordering_key to additional @@ -1131,6 +1132,10 @@ class BigQueryConfig(proto.Message): Output only. An output-only field that indicates whether or not the subscription can receive messages. + use_table_schema (bool): + Optional. When true, use the BigQuery table's schema as the + columns to write to in BigQuery. ``use_table_schema`` and + ``use_topic_schema`` cannot be enabled at the same time. """ class State(proto.Enum): @@ -1186,6 +1191,10 @@ class State(proto.Enum): number=5, enum=State, ) + use_table_schema: bool = proto.Field( + proto.BOOL, + number=6, + ) class CloudStorageConfig(proto.Message): From a82e80d55902e1998ac74e33ea8b20a901d8ff93 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Tue, 5 Dec 2023 09:52:19 -0500 Subject: [PATCH 009/190] build: treat warnings as errors (#1033) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * build: treat warnings as errors * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * lint * fix warning in samples * remove warning from pytest.ini which doesn't have a tracking bug --------- Co-authored-by: Owl Bot --- pytest.ini | 17 +++++++++++++++++ samples/snippets/schema.py | 9 ++++++--- tests/unit/gapic/pubsub_v1/test_subscriber.py | 4 ---- .../pubsub_v1/publisher/test_flow_controller.py | 8 +++++--- .../publisher/test_publisher_client.py | 5 ++--- .../pubsub_v1/subscriber/test_dispatcher.py | 3 ++- .../unit/pubsub_v1/subscriber/test_scheduler.py | 6 ++++-- .../subscriber/test_subscriber_client.py | 17 ++++++++++------- 8 files changed, 46 insertions(+), 23 deletions(-) create mode 100644 pytest.ini diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 000000000..fd477ac99 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,17 @@ +[pytest] +filterwarnings = + # treat all warnings as errors + error + # Remove once https://github.com/protocolbuffers/protobuf/issues/12186 is fixed + ignore:.*custom tp_new.*in Python 3.14:DeprecationWarning + # Remove once https://github.com/googleapis/python-api-common-protos/pull/191 is merged + ignore:.*pkg_resources.declare_namespace:DeprecationWarning + ignore:.*pkg_resources is deprecated as an API:DeprecationWarning + # Remove once release PR https://github.com/googleapis/proto-plus-python/pull/391 is merged + ignore:datetime.datetime.utcfromtimestamp\(\) is deprecated:DeprecationWarning:proto.datetime_helpers + # Remove once release PR https://github.com/googleapis/python-api-core/pull/555 is merged + ignore:datetime.datetime.utcnow\(\) is deprecated:DeprecationWarning:google.api_core.datetime_helpers + # Remove once https://github.com/grpc/grpc/issues/35086 is fixed + ignore:There is no current event loop:DeprecationWarning:grpc.aio._channel + # Remove once a version of grpcio newer than 1.59.3 is released to PyPI + ignore:datetime.datetime.utcnow\(\) is deprecated:DeprecationWarning:grpc._channel diff --git a/samples/snippets/schema.py b/samples/snippets/schema.py index 40cd853c9..3260a0e19 100644 --- a/samples/snippets/schema.py +++ b/samples/snippets/schema.py @@ -473,7 +473,8 @@ def publish_avro_records(project_id: str, topic_id: str, avsc_file: str) -> None topic_path = publisher_client.topic_path(project_id, topic_id) # Prepare to write Avro records to the binary output stream. - avro_schema = schema.parse(open(avsc_file, "rb").read()) + with open(avsc_file, "rb") as file: + avro_schema = schema.parse(file.read()) writer = DatumWriter(avro_schema) bout = io.BytesIO() @@ -579,7 +580,8 @@ def subscribe_with_avro_schema( subscriber = SubscriberClient() subscription_path = subscriber.subscription_path(project_id, subscription_id) - avro_schema = schema.parse(open(avsc_file, "rb").read()) + with open(avsc_file, "rb") as file: + avro_schema = schema.parse(file.read()) def callback(message: pubsub_v1.subscriber.message.Message) -> None: # Get the message serialization type. @@ -642,7 +644,8 @@ def subscribe_with_avro_schema_with_revisions( subscriber = SubscriberClient() subscription_path = subscriber.subscription_path(project_id, subscription_id) - writer_avro_schema = schema.parse(open(avsc_file, "rb").read()) + with open(avsc_file, "rb") as file: + writer_avro_schema = schema.parse(file.read()) # Dict to keep readers for different schema revisions. revisions_to_readers = {} diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py index c31b98cd0..94f7e301e 100644 --- a/tests/unit/gapic/pubsub_v1/test_subscriber.py +++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py @@ -2888,7 +2888,6 @@ def test_pull_flattened_error(): client.pull( pubsub.PullRequest(), subscription="subscription_value", - return_immediately=True, max_messages=1277, ) @@ -2942,7 +2941,6 @@ async def test_pull_flattened_error_async(): await client.pull( pubsub.PullRequest(), subscription="subscription_value", - return_immediately=True, max_messages=1277, ) @@ -6993,7 +6991,6 @@ def test_pull_rest_flattened(): # get truthy value for each flattened field mock_args = dict( subscription="subscription_value", - return_immediately=True, max_messages=1277, ) mock_args.update(sample_request) @@ -7032,7 +7029,6 @@ def test_pull_rest_flattened_error(transport: str = "rest"): client.pull( pubsub.PullRequest(), subscription="subscription_value", - return_immediately=True, max_messages=1277, ) diff --git a/tests/unit/pubsub_v1/publisher/test_flow_controller.py b/tests/unit/pubsub_v1/publisher/test_flow_controller.py index ee923a435..776c6db41 100644 --- a/tests/unit/pubsub_v1/publisher/test_flow_controller.py +++ b/tests/unit/pubsub_v1/publisher/test_flow_controller.py @@ -19,7 +19,6 @@ from typing import Callable from typing import Sequence from typing import Union -import warnings import pytest @@ -179,7 +178,10 @@ def test_incorrectly_releasing_too_many_messages(): msg3 = grpc_types.PubsubMessage(data=b"z" * 100) # Releasing a message that would make the load negative should result in a warning. - with warnings.catch_warnings(record=True) as warned: + with pytest.warns( + RuntimeWarning, + match="Releasing a message that was never added or already released", + ) as warned: flow_controller.release(msg1) assert len(warned) == 1 @@ -438,7 +440,7 @@ def test_warning_on_internal_reservation_stats_error_when_unblocking(): assert reservation is not None, "No messages blocked by flow controller." reservation.bytes_reserved = reservation.bytes_needed + 1 - with warnings.catch_warnings(record=True) as warned: + with pytest.warns(RuntimeWarning, match="Too many bytes reserved.") as warned: _run_in_daemon(flow_controller.release, [msg1], releasing_1_done) if not releasing_1_done.wait(timeout=0.1): pytest.fail("Releasing a message blocked or errored.") # pragma: NO COVER diff --git a/tests/unit/pubsub_v1/publisher/test_publisher_client.py b/tests/unit/pubsub_v1/publisher/test_publisher_client.py index 91c556cd6..5d6013654 100644 --- a/tests/unit/pubsub_v1/publisher/test_publisher_client.py +++ b/tests/unit/pubsub_v1/publisher/test_publisher_client.py @@ -28,7 +28,6 @@ import pytest import time -import warnings from google.api_core import gapic_v1 from google.api_core import retry as retries @@ -60,7 +59,7 @@ def _assert_retries_equal(retry, retry2): def test_api_property_deprecated(creds): client = publisher.Client(credentials=creds) - with warnings.catch_warnings(record=True) as warned: + with pytest.warns(DeprecationWarning, match="client.api") as warned: client.api assert len(warned) == 1 @@ -72,7 +71,7 @@ def test_api_property_deprecated(creds): def test_api_property_proxy_to_generated_client(creds): client = publisher.Client(credentials=creds) - with warnings.catch_warnings(record=True): + with pytest.warns(DeprecationWarning, match="client.api"): api_object = client.api # Not a perfect check, but we are satisficed if the returned API object indeed diff --git a/tests/unit/pubsub_v1/subscriber/test_dispatcher.py b/tests/unit/pubsub_v1/subscriber/test_dispatcher.py index d4813911c..89d72c61d 100644 --- a/tests/unit/pubsub_v1/subscriber/test_dispatcher.py +++ b/tests/unit/pubsub_v1/subscriber/test_dispatcher.py @@ -361,7 +361,8 @@ def test_unknown_request_type(): items = ["a random string, not a known request type"] manager.send_unary_ack.return_value = (items, []) - dispatcher_.dispatch_callback(items) + with pytest.warns(RuntimeWarning, match="Skipping unknown request item of type"): + dispatcher_.dispatch_callback(items) def test_ack(): diff --git a/tests/unit/pubsub_v1/subscriber/test_scheduler.py b/tests/unit/pubsub_v1/subscriber/test_scheduler.py index ff76fa09d..3ed1978c1 100644 --- a/tests/unit/pubsub_v1/subscriber/test_scheduler.py +++ b/tests/unit/pubsub_v1/subscriber/test_scheduler.py @@ -14,10 +14,10 @@ import concurrent.futures import queue +import pytest import sys import threading import time -import warnings # special case python < 3.8 if sys.version_info.major == 3 and sys.version_info.minor < 8: @@ -76,7 +76,9 @@ def callback(*args, **kwargs): scheduler_.schedule(callback, "arg1", kwarg1="meep") scheduler_._executor.shutdown() - with warnings.catch_warnings(record=True) as warned: + with pytest.warns( + RuntimeWarning, match="Scheduling a callback after executor shutdown" + ) as warned: scheduler_.schedule(callback, "arg2", kwarg2="boop") assert len(warned) == 1 diff --git a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py index 83ef3f06d..bedfde79a 100644 --- a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py +++ b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py @@ -13,7 +13,6 @@ # limitations under the License. import sys -import warnings import grpc @@ -218,7 +217,7 @@ def test_context_manager_raises_if_closed(creds): def test_api_property_deprecated(creds): client = subscriber.Client(credentials=creds) - with warnings.catch_warnings(record=True) as warned: + with pytest.warns(DeprecationWarning, match="client.api") as warned: client.api assert len(warned) == 1 @@ -230,7 +229,7 @@ def test_api_property_deprecated(creds): def test_api_property_proxy_to_generated_client(creds): client = subscriber.Client(credentials=creds) - with warnings.catch_warnings(record=True): + with pytest.warns(DeprecationWarning, match="client.api"): api_object = client.api # Not a perfect check, but we are satisficed if the returned API object indeed @@ -262,9 +261,10 @@ def test_sync_pull_warning_if_return_immediately(creds): client = subscriber.Client(credentials=creds) subscription_path = "projects/foo/subscriptions/bar" - with mock.patch.object( - client._transport, "_wrapped_methods" - ), warnings.catch_warnings(record=True) as warned: + with mock.patch.object(client._transport, "_wrapped_methods"), pytest.warns( + DeprecationWarning, + match="The return_immediately flag is deprecated and should be set to False", + ) as warned: client.pull(subscription=subscription_path, return_immediately=True) # Setting the deprecated return_immediately flag to True should emit a warning. @@ -287,7 +287,10 @@ async def test_sync_pull_warning_if_return_immediately_async(creds): new=mock.AsyncMock, ) - with patcher, warnings.catch_warnings(record=True) as warned: + with patcher, pytest.warns( + DeprecationWarning, + match="The return_immediately flag is deprecated and should be set to False", + ) as warned: await client.pull(subscription=subscription_path, return_immediately=True) # Setting the deprecated return_immediately flag to True should emit a warning. From 2295a24ab894b5e009adb4d315046e7b08a2d856 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Sun, 10 Dec 2023 09:31:28 -0500 Subject: [PATCH 010/190] build: update actions/checkout and actions/setup-python (#1040) * build: update actions/checkout and actions/setup-python Source-Link: https://github.com/googleapis/synthtool/commit/3551acd1261fd8f616cbfd054cda9bd6d6ac75f4 Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:230f7fe8a0d2ed81a519cfc15c6bb11c5b46b9fb449b8b1219b3771bcb520ad2 * build: update warning filters --------- Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- .github/.OwlBot.lock.yaml | 4 ++-- .github/workflows/docs.yml | 8 ++++---- .github/workflows/lint.yml | 4 ++-- .github/workflows/unittest.yml | 8 ++++---- pytest.ini | 11 ++--------- 5 files changed, 14 insertions(+), 21 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 773c1dfd2..40bf99731 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:2f155882785883336b4468d5218db737bb1d10c9cea7cb62219ad16fe248c03c -# created: 2023-11-29T14:54:29.548172703Z + digest: sha256:230f7fe8a0d2ed81a519cfc15c6bb11c5b46b9fb449b8b1219b3771bcb520ad2 +# created: 2023-12-09T15:16:25.430769578Z diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 221806ced..698fbc5c9 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -8,9 +8,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.9" - name: Install nox @@ -24,9 +24,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.10" - name: Install nox diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 16d5a9e90..4866193af 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -8,9 +8,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.8" - name: Install nox diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index a32027b49..d6ca65627 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -11,9 +11,9 @@ jobs: python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - name: Install nox @@ -37,9 +37,9 @@ jobs: - unit steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.8" - name: Install coverage diff --git a/pytest.ini b/pytest.ini index fd477ac99..49de304e3 100644 --- a/pytest.ini +++ b/pytest.ini @@ -4,14 +4,7 @@ filterwarnings = error # Remove once https://github.com/protocolbuffers/protobuf/issues/12186 is fixed ignore:.*custom tp_new.*in Python 3.14:DeprecationWarning - # Remove once https://github.com/googleapis/python-api-common-protos/pull/191 is merged - ignore:.*pkg_resources.declare_namespace:DeprecationWarning - ignore:.*pkg_resources is deprecated as an API:DeprecationWarning - # Remove once release PR https://github.com/googleapis/proto-plus-python/pull/391 is merged - ignore:datetime.datetime.utcfromtimestamp\(\) is deprecated:DeprecationWarning:proto.datetime_helpers - # Remove once release PR https://github.com/googleapis/python-api-core/pull/555 is merged - ignore:datetime.datetime.utcnow\(\) is deprecated:DeprecationWarning:google.api_core.datetime_helpers # Remove once https://github.com/grpc/grpc/issues/35086 is fixed ignore:There is no current event loop:DeprecationWarning:grpc.aio._channel - # Remove once a version of grpcio newer than 1.59.3 is released to PyPI - ignore:datetime.datetime.utcnow\(\) is deprecated:DeprecationWarning:grpc._channel + # Remove after support for Python 3.7 is dropped + ignore:After January 1, 2024, new releases of this library will drop support for Python 3.7:DeprecationWarning \ No newline at end of file From 7b17647acad33b0c4070917016b9b2b703284e87 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 14:15:13 -0500 Subject: [PATCH 011/190] chore(main): release 2.19.0 (#1031) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7f3c731be..36be43f26 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.18.4" + ".": "2.19.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b0d33b1c5..5c54270b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.19.0](https://github.com/googleapis/python-pubsub/compare/v2.18.4...v2.19.0) (2023-12-10) + + +### Features + +* Add `use_table_schema` field to BigQueryConfig ([#1035](https://github.com/googleapis/python-pubsub/issues/1035)) ([ac6d912](https://github.com/googleapis/python-pubsub/commit/ac6d9126413b5c8e2b00727f7d74f03b7fb9d9ed)) +* Add support for Python 3.12 ([#1025](https://github.com/googleapis/python-pubsub/issues/1025)) ([660b8ea](https://github.com/googleapis/python-pubsub/commit/660b8eaf0daf975834a8333aedf8415867a4874d)) +* Introduce compatibility with native namespace packages ([#1024](https://github.com/googleapis/python-pubsub/issues/1024)) ([0432420](https://github.com/googleapis/python-pubsub/commit/0432420dcf18304dc1912075482eff0d2dc73009)) + + +### Bug Fixes + +* Use `retry_async` instead of `retry` in async client ([#1030](https://github.com/googleapis/python-pubsub/issues/1030)) ([05dd571](https://github.com/googleapis/python-pubsub/commit/05dd571760b71ae2930072f0677616dfc19d9511)) + ## [2.18.4](https://github.com/googleapis/python-pubsub/compare/v2.18.3...v2.18.4) (2023-09-09) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 5b1f1ec81..0f1a446f3 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.18.4" # {x-release-please-version} +__version__ = "2.19.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 5b1f1ec81..0f1a446f3 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.18.4" # {x-release-please-version} +__version__ = "2.19.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index d66015ac4..25ee6d056 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "0.1.0" + "version": "2.19.0" }, "snippets": [ { From a0eba77cb5e2bc1a8d1605d4e35ba67c84488b32 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Thu, 14 Dec 2023 13:27:56 +0100 Subject: [PATCH 012/190] chore(deps): update all dependencies (#1050) --- samples/snippets/requirements-test.txt | 4 ++-- samples/snippets/requirements.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index b3325ed9f..17e317ce3 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -2,5 +2,5 @@ backoff==2.2.1 pytest==7.4.3 mock==5.1.0 flaky==3.7.0 -google-cloud-bigquery==3.13.0 -google-cloud-storage==2.13.0 +google-cloud-bigquery==3.14.1 +google-cloud-storage==2.14.0 diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index a3124ddce..3fb4e0a69 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,4 +1,4 @@ -google-cloud-pubsub==2.18.4 +google-cloud-pubsub==2.19.0 avro==1.11.3 protobuf===4.24.4; python_version == '3.7' protobuf==4.25.1; python_version >= '3.8' From 062c132e20d464ba30169384c24521189f9b1b36 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 19:18:32 -0500 Subject: [PATCH 013/190] build: update actions/upload-artifact and actions/download-artifact (#1053) Source-Link: https://github.com/googleapis/synthtool/commit/280ddaed417057dfe5b1395731de07b7d09f5058 Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:346ab2efb51649c5dde7756cbbdc60dd394852ba83b9bbffc292a63549f33c17 Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 ++-- .github/workflows/unittest.yml | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 40bf99731..9bee24097 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:230f7fe8a0d2ed81a519cfc15c6bb11c5b46b9fb449b8b1219b3771bcb520ad2 -# created: 2023-12-09T15:16:25.430769578Z + digest: sha256:346ab2efb51649c5dde7756cbbdc60dd394852ba83b9bbffc292a63549f33c17 +# created: 2023-12-14T22:17:57.611773021Z diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index d6ca65627..f4a337c49 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -26,9 +26,9 @@ jobs: run: | nox -s unit-${{ matrix.python }} - name: Upload coverage results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: coverage-artifacts + name: coverage-artifact-${{ matrix.python }} path: .coverage-${{ matrix.python }} cover: @@ -47,11 +47,11 @@ jobs: python -m pip install --upgrade setuptools pip wheel python -m pip install coverage - name: Download coverage results - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: - name: coverage-artifacts path: .coverage-results/ - name: Report coverage results run: | - coverage combine .coverage-results/.coverage* + find .coverage-results -type f -name '*.zip' -exec unzip {} \; + coverage combine .coverage-results/**/.coverage* coverage report --show-missing --fail-under=100 From 265f4106f499ec5d2d01a127ba192404c1836a28 Mon Sep 17 00:00:00 2001 From: Kamal Aboul-Hosn Date: Fri, 15 Dec 2023 12:41:38 -0500 Subject: [PATCH 014/190] fix: Swap writer and reader schema to correct places in sample (#1052) * samples: schema evolution * Add command-line commands * Fix tag for rollback * Make formatting fixes * Formatting fixes * Fix exceptions * fix: Set x-goog-request-params for streaming pull request * Set blunderbuss config to auto-assign issues and PRs * fix: Swap writer and reader schema to correct places in sample --- samples/snippets/schema.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/snippets/schema.py b/samples/snippets/schema.py index 3260a0e19..b492ccf33 100644 --- a/samples/snippets/schema.py +++ b/samples/snippets/schema.py @@ -645,7 +645,7 @@ def subscribe_with_avro_schema_with_revisions( subscription_path = subscriber.subscription_path(project_id, subscription_id) with open(avsc_file, "rb") as file: - writer_avro_schema = schema.parse(file.read()) + reader_avro_schema = schema.parse(file.read()) # Dict to keep readers for different schema revisions. revisions_to_readers = {} @@ -665,7 +665,7 @@ def callback(message: pubsub_v1.subscriber.message.Message) -> None: print(f"{schema_path} not found.") message.nack() return - reader_avro_schema = schema.parse(received_avro_schema.definition) + writer_avro_schema = schema.parse(received_avro_schema.definition) revisions_to_readers[schema_revision_id] = DatumReader( writer_avro_schema, reader_avro_schema ) From 0562d23f1116219037111432bc4d49f27b879efb Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:14:08 -0500 Subject: [PATCH 015/190] build(python): fix `docs` and `docfx` builds (#1063) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 * exclude types-protobuf==4.24.0.20240106 release * typo * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * exclude types-protobuf==4.24.0.20240106 release * update replacement * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- .github/.OwlBot.lock.yaml | 6 +++--- .kokoro/requirements.txt | 6 +++--- noxfile.py | 30 +++++++++++++++++++++++++----- owlbot.py | 10 ++++++---- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 9bee24097..d8a1bbca7 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:346ab2efb51649c5dde7756cbbdc60dd394852ba83b9bbffc292a63549f33c17 -# created: 2023-12-14T22:17:57.611773021Z + digest: sha256:5ea6d0ab82c956b50962f91d94e206d3921537ae5fe1549ec5326381d8905cfa +# created: 2024-01-15T16:32:08.142785673Z diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index e5c1ffca9..bb3d6ca38 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -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/noxfile.py b/noxfile.py index 3f9db8b2c..ec63afcb4 100644 --- a/noxfile.py +++ b/noxfile.py @@ -92,10 +92,6 @@ def mypy(session): session.install("-e", ".[all]") session.install(MYPY_VERSION) - # Just install the type info directly, since "mypy --install-types" might - # require an additional pass. - session.install("types-protobuf", "types-setuptools") - # Version 2.1.1 of google-api-core version is the first type-checked release. # Version 2.2.0 of google-cloud-core version is the first type-checked release. session.install( @@ -103,6 +99,12 @@ def mypy(session): "google-cloud-core>=2.2.0", ) + # Just install the type info directly, since "mypy --install-types" might + # require an additional pass. + # Exclude types-protobuf==4.24.0.20240106 + # See https://github.com/python/typeshed/issues/11254 + session.install("types-protobuf!=4.24.0.20240106", "types-setuptools") + # TODO: Only check the hand-written layer, the generated code does not pass # mypy checks yet. # https://github.com/googleapis/gapic-generator-python/issues/1092 @@ -334,7 +336,16 @@ def docs(session): session.install("-e", ".") session.install( - "sphinx==4.0.1", + # 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.5.0", "alabaster", "recommonmark", ) @@ -360,6 +371,15 @@ def docfx(session): session.install("-e", ".") 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", "gcp-sphinx-docfx-yaml", "alabaster", "recommonmark", diff --git a/owlbot.py b/owlbot.py index 338687588..0483b04df 100644 --- a/owlbot.py +++ b/owlbot.py @@ -366,10 +366,6 @@ def mypy(session): session.install("-e", ".[all]") session.install(MYPY_VERSION) - # Just install the type info directly, since "mypy --install-types" might - # require an additional pass. - session.install("types-protobuf", "types-setuptools") - # Version 2.1.1 of google-api-core version is the first type-checked release. # Version 2.2.0 of google-cloud-core version is the first type-checked release. session.install( @@ -377,6 +373,12 @@ def mypy(session): "google-cloud-core>=2.2.0", ) + # Just install the type info directly, since "mypy --install-types" might + # require an additional pass. + # Exclude types-protobuf==4.24.0.20240106 + # See https://github.com/python/typeshed/issues/11254 + session.install("types-protobuf!=4.24.0.20240106", "types-setuptools") + # TODO: Only check the hand-written layer, the generated code does not pass # mypy checks yet. # https://github.com/googleapis/gapic-generator-python/issues/1092 From b4442c644d283ff02eb3cdd4c3367e8fa4867759 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Fri, 2 Feb 2024 11:09:45 -0500 Subject: [PATCH 016/190] test: Silence return_immediately flag Deprecation Warning (#1066) --- pytest.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index 49de304e3..e612d31f1 100644 --- a/pytest.ini +++ b/pytest.ini @@ -7,4 +7,6 @@ filterwarnings = # Remove once https://github.com/grpc/grpc/issues/35086 is fixed ignore:There is no current event loop:DeprecationWarning:grpc.aio._channel # Remove after support for Python 3.7 is dropped - ignore:After January 1, 2024, new releases of this library will drop support for Python 3.7:DeprecationWarning \ No newline at end of file + ignore:After January 1, 2024, new releases of this library will drop support for Python 3.7:DeprecationWarning + # Remove warning once https://github.com/googleapis/python-pubsub/issues/1067 is fixed + ignore:The return_immediately flag is deprecated and should be set to False.:DeprecationWarning \ No newline at end of file From e80342f3c7e319dbb478115e65b0ee4d30cc731b Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 11:45:27 -0500 Subject: [PATCH 017/190] chore(main): release 2.19.1 (#1054) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 36be43f26..ffffd9a18 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.19.0" + ".": "2.19.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c54270b9..721fdf367 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.19.1](https://github.com/googleapis/python-pubsub/compare/v2.19.0...v2.19.1) (2024-02-02) + + +### Documentation + +* **samples:** Swap writer and reader schema to correct places ([265f410](https://github.com/googleapis/python-pubsub/commit/265f4106f499ec5d2d01a127ba192404c1836a28)) + ## [2.19.0](https://github.com/googleapis/python-pubsub/compare/v2.18.4...v2.19.0) (2023-12-10) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 0f1a446f3..c5d0439f6 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.0" # {x-release-please-version} +__version__ = "2.19.1" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 0f1a446f3..c5d0439f6 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.0" # {x-release-please-version} +__version__ = "2.19.1" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 25ee6d056..32f3378f9 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.19.0" + "version": "2.19.1" }, "snippets": [ { From dcd5fe56eae2fe7c1612d1fa4a8a56bc64436a5a Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 09:57:47 -0500 Subject: [PATCH 018/190] build(deps): bump cryptography from 41.0.6 to 42.0.0 in /synthtool/gcp/templates/python_library/.kokoro (#1071) 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 d8a1bbca7..2aefd0e91 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:97b671488ad548ef783a452a9e1276ac10f144d5ae56d98cc4bf77ba504082b4 +# created: 2024-02-06T03:20:16.660474034Z diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index bb3d6ca38..8c11c9f3e 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.0 \ + --hash=sha256:0a68bfcf57a6887818307600c3c0ebc3f62fbb6ccad2240aa21887cda1f8df1b \ + --hash=sha256:146e971e92a6dd042214b537a726c9750496128453146ab0ee8971a0299dc9bd \ + --hash=sha256:14e4b909373bc5bf1095311fa0f7fcabf2d1a160ca13f1e9e467be1ac4cbdf94 \ + --hash=sha256:206aaf42e031b93f86ad60f9f5d9da1b09164f25488238ac1dc488334eb5e221 \ + --hash=sha256:3005166a39b70c8b94455fdbe78d87a444da31ff70de3331cdec2c568cf25b7e \ + --hash=sha256:324721d93b998cb7367f1e6897370644751e5580ff9b370c0a50dc60a2003513 \ + --hash=sha256:33588310b5c886dfb87dba5f013b8d27df7ffd31dc753775342a1e5ab139e59d \ + --hash=sha256:35cf6ed4c38f054478a9df14f03c1169bb14bd98f0b1705751079b25e1cb58bc \ + --hash=sha256:3ca482ea80626048975360c8e62be3ceb0f11803180b73163acd24bf014133a0 \ + --hash=sha256:56ce0c106d5c3fec1038c3cca3d55ac320a5be1b44bf15116732d0bc716979a2 \ + --hash=sha256:5a217bca51f3b91971400890905a9323ad805838ca3fa1e202a01844f485ee87 \ + --hash=sha256:678cfa0d1e72ef41d48993a7be75a76b0725d29b820ff3cfd606a5b2b33fda01 \ + --hash=sha256:69fd009a325cad6fbfd5b04c711a4da563c6c4854fc4c9544bff3088387c77c0 \ + --hash=sha256:6cf9b76d6e93c62114bd19485e5cb003115c134cf9ce91f8ac924c44f8c8c3f4 \ + --hash=sha256:74f18a4c8ca04134d2052a140322002fef535c99cdbc2a6afc18a8024d5c9d5b \ + --hash=sha256:85f759ed59ffd1d0baad296e72780aa62ff8a71f94dc1ab340386a1207d0ea81 \ + --hash=sha256:87086eae86a700307b544625e3ba11cc600c3c0ef8ab97b0fda0705d6db3d4e3 \ + --hash=sha256:8814722cffcfd1fbd91edd9f3451b88a8f26a5fd41b28c1c9193949d1c689dc4 \ + --hash=sha256:8fedec73d590fd30c4e3f0d0f4bc961aeca8390c72f3eaa1a0874d180e868ddf \ + --hash=sha256:9515ea7f596c8092fdc9902627e51b23a75daa2c7815ed5aa8cf4f07469212ec \ + --hash=sha256:988b738f56c665366b1e4bfd9045c3efae89ee366ca3839cd5af53eaa1401bce \ + --hash=sha256:a2a8d873667e4fd2f34aedab02ba500b824692c6542e017075a2efc38f60a4c0 \ + --hash=sha256:bd7cf7a8d9f34cc67220f1195884151426ce616fdc8285df9054bfa10135925f \ + --hash=sha256:bdce70e562c69bb089523e75ef1d9625b7417c6297a76ac27b1b8b1eb51b7d0f \ + --hash=sha256:be14b31eb3a293fc6e6aa2807c8a3224c71426f7c4e3639ccf1a2f3ffd6df8c3 \ + --hash=sha256:be41b0c7366e5549265adf2145135dca107718fa44b6e418dc7499cfff6b4689 \ + --hash=sha256:c310767268d88803b653fffe6d6f2f17bb9d49ffceb8d70aed50ad45ea49ab08 \ + --hash=sha256:c58115384bdcfe9c7f644c72f10f6f42bed7cf59f7b52fe1bf7ae0a622b3a139 \ + --hash=sha256:c640b0ef54138fde761ec99a6c7dc4ce05e80420262c20fa239e694ca371d434 \ + --hash=sha256:ca20550bb590db16223eb9ccc5852335b48b8f597e2f6f0878bbfd9e7314eb17 \ + --hash=sha256:d97aae66b7de41cdf5b12087b5509e4e9805ed6f562406dfcf60e8481a9a28f8 \ + --hash=sha256:e9326ca78111e4c645f7e49cbce4ed2f3f85e17b61a563328c85a5208cf34440 # via # gcp-releasetool # secretstorage From bd0a3bf6fb86883e9a954283caf444f2296624e9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 16:35:13 -0500 Subject: [PATCH 019/190] build(deps): bump cryptography from 41.0.6 to 42.0.0 in /.kokoro (#1070) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Owl Bot Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> From 3c6d128a53d83439036aaec1f1fd48331152935b Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Thu, 8 Feb 2024 14:02:36 -0500 Subject: [PATCH 020/190] =?UTF-8?q?fix:=20unit=20test=20failures=20in=20ht?= =?UTF-8?q?tps://github.com/googleapis/python-pubsu=E2=80=A6=20(#1074)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pytest.ini | 6 ++++-- tests/unit/pubsub_v1/conftest.py | 10 +--------- tests/unit/pubsub_v1/publisher/batch/test_base.py | 9 +-------- tests/unit/pubsub_v1/publisher/batch/test_thread.py | 3 +-- .../publisher/sequencer/test_ordered_sequencer.py | 3 +-- .../publisher/sequencer/test_unordered_sequencer.py | 3 +-- 6 files changed, 9 insertions(+), 25 deletions(-) diff --git a/pytest.ini b/pytest.ini index e612d31f1..7308b0871 100644 --- a/pytest.ini +++ b/pytest.ini @@ -8,5 +8,7 @@ filterwarnings = ignore:There is no current event loop:DeprecationWarning:grpc.aio._channel # Remove after support for Python 3.7 is dropped ignore:After January 1, 2024, new releases of this library will drop support for Python 3.7:DeprecationWarning - # Remove warning once https://github.com/googleapis/python-pubsub/issues/1067 is fixed - ignore:The return_immediately flag is deprecated and should be set to False.:DeprecationWarning \ No newline at end of file + # Remove warning once https://github.com/googleapis/gapic-generator-python/issues/1938 is fixed + ignore:The return_immediately flag is deprecated and should be set to False.:DeprecationWarning + # Remove warning once https://github.com/googleapis/gapic-generator-python/issues/1939 is fixed + ignore:get_mtls_endpoint_and_cert_source is deprecated.:DeprecationWarning \ No newline at end of file diff --git a/tests/unit/pubsub_v1/conftest.py b/tests/unit/pubsub_v1/conftest.py index e39832d5f..dc4192931 100644 --- a/tests/unit/pubsub_v1/conftest.py +++ b/tests/unit/pubsub_v1/conftest.py @@ -11,16 +11,8 @@ # 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 sys import google.auth.credentials - -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock - import pytest @@ -30,4 +22,4 @@ def creds(): Provide test creds to unit tests so that they can run without GOOGLE_APPLICATION_CREDENTIALS set. """ - yield mock.Mock(spec=google.auth.credentials.Credentials) + yield google.auth.credentials.AnonymousCredentials() diff --git a/tests/unit/pubsub_v1/publisher/batch/test_base.py b/tests/unit/pubsub_v1/publisher/batch/test_base.py index c35f482e7..a95d72c12 100644 --- a/tests/unit/pubsub_v1/publisher/batch/test_base.py +++ b/tests/unit/pubsub_v1/publisher/batch/test_base.py @@ -13,13 +13,7 @@ # limitations under the License. from __future__ import absolute_import -import sys -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock from google.auth import credentials from google.cloud.pubsub_v1 import publisher @@ -38,8 +32,7 @@ def create_batch(status, settings=types.BatchSettings()): Returns: ~.pubsub_v1.publisher.batch.thread.Batch: The batch object """ - creds = mock.Mock(spec=credentials.Credentials) - client = publisher.Client(credentials=creds) + client = publisher.Client(credentials=credentials.AnonymousCredentials()) batch = Batch(client, "topic_name", settings) batch._status = status return batch diff --git a/tests/unit/pubsub_v1/publisher/batch/test_thread.py b/tests/unit/pubsub_v1/publisher/batch/test_thread.py index 60658b4ce..2752d62a2 100644 --- a/tests/unit/pubsub_v1/publisher/batch/test_thread.py +++ b/tests/unit/pubsub_v1/publisher/batch/test_thread.py @@ -39,8 +39,7 @@ def create_client(): - creds = mock.Mock(spec=credentials.Credentials) - return publisher.Client(credentials=creds) + return publisher.Client(credentials=credentials.AnonymousCredentials()) def create_batch( diff --git a/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py b/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py index e126c829f..7570c2970 100644 --- a/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py +++ b/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py @@ -36,8 +36,7 @@ def create_message(): def create_client(): - creds = mock.Mock(spec=credentials.Credentials) - return publisher.Client(credentials=creds) + return publisher.Client(credentials=credentials.AnonymousCredentials()) def create_ordered_sequencer(client): diff --git a/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py b/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py index 8a2c486ad..01d9d6ca4 100644 --- a/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py +++ b/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py @@ -34,8 +34,7 @@ def create_message(): def create_client(): - creds = mock.Mock(spec=credentials.Credentials) - return publisher.Client(credentials=creds) + return publisher.Client(credentials=credentials.AnonymousCredentials()) def test_stop(): From 5f5940511461149dfa37d2941d24935ccd6b3ae8 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 11:48:15 -0800 Subject: [PATCH 021/190] chore(main): release 2.19.2 (#1075) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ffffd9a18..4c00940a5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.19.1" + ".": "2.19.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 721fdf367..31c167a02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.19.2](https://github.com/googleapis/python-pubsub/compare/v2.19.1...v2.19.2) (2024-02-08) + + +### Bug Fixes + +* Unit test failures in https://github.com/googleapis/python-pubsu… ([#1074](https://github.com/googleapis/python-pubsub/issues/1074)) ([3c6d128](https://github.com/googleapis/python-pubsub/commit/3c6d128a53d83439036aaec1f1fd48331152935b)) + ## [2.19.1](https://github.com/googleapis/python-pubsub/compare/v2.19.0...v2.19.1) (2024-02-02) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index c5d0439f6..72b71f9cd 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.1" # {x-release-please-version} +__version__ = "2.19.2" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index c5d0439f6..72b71f9cd 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.1" # {x-release-please-version} +__version__ = "2.19.2" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 32f3378f9..fb45cf3da 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.19.1" + "version": "2.19.2" }, "snippets": [ { From f0a1b639351581d5b8405c3857f8310d82492af2 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Thu, 8 Feb 2024 16:16:29 -0500 Subject: [PATCH 022/190] chore: change assignees for issues and PRs to mukund-ananthu (#1072) --- .github/blunderbuss.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/blunderbuss.yml b/.github/blunderbuss.yml index 687c7f91b..992f04dce 100644 --- a/.github/blunderbuss.yml +++ b/.github/blunderbuss.yml @@ -1,6 +1,6 @@ # Configuration for the Blunderbuss GitHub app. For more info see # https://github.com/googleapis/repo-automation-bots/tree/main/packages/blunderbuss assign_issues: - - pradn + - mukund-ananthu assign_prs: - - pradn \ No newline at end of file + - mukund-ananthu From 5ce7301b3056191203bc89bbcf1f33083de72a2d Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:47:53 -0500 Subject: [PATCH 023/190] fix: add google-auth as a direct dependency (#1076) --- setup.py | 1 + testing/constraints-3.7.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/setup.py b/setup.py index d388d9443..a6af31207 100644 --- a/setup.py +++ b/setup.py @@ -38,6 +38,7 @@ dependencies = [ "grpcio >= 1.51.3, < 2.0dev", # https://github.com/googleapis/python-pubsub/issues/609 # google-api-core >= 1.34.0 is allowed in order to support google-api-core 1.x + "google-auth >= 2.14.1, <3.0.0dev", "google-api-core[grpc] >= 1.34.0, <3.0.0dev,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,!=2.10.*", "proto-plus >= 1.22.0, <2.0.0dev", "proto-plus >= 1.22.2, <2.0.0dev; python_version>='3.11'", diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt index 75ec7a623..ee447eb62 100644 --- a/testing/constraints-3.7.txt +++ b/testing/constraints-3.7.txt @@ -5,6 +5,7 @@ # e.g., if setup.py has "google-cloud-foo >= 1.14.0, < 2.0.0dev", # Then this file should have google-cloud-foo==1.14.0 google-api-core==1.34.0 +google-auth==2.14.1 proto-plus==1.22.0 protobuf==3.19.5 grpc-google-iam-v1==0.12.4 From aa40c4b409f7bff3dea6747dd32d6aef49dd70be Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Fri, 9 Feb 2024 11:02:57 -0500 Subject: [PATCH 024/190] chore(main): release 2.19.3 (#1077) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 4c00940a5..a31cb5941 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.19.2" + ".": "2.19.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 31c167a02..d9f24c15c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.19.3](https://github.com/googleapis/python-pubsub/compare/v2.19.2...v2.19.3) (2024-02-08) + + +### Bug Fixes + +* Add google-auth as a direct dependency ([#1076](https://github.com/googleapis/python-pubsub/issues/1076)) ([5ce7301](https://github.com/googleapis/python-pubsub/commit/5ce7301b3056191203bc89bbcf1f33083de72a2d)) + ## [2.19.2](https://github.com/googleapis/python-pubsub/compare/v2.19.1...v2.19.2) (2024-02-08) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 72b71f9cd..3feb0e097 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.2" # {x-release-please-version} +__version__ = "2.19.3" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 72b71f9cd..3feb0e097 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.2" # {x-release-please-version} +__version__ = "2.19.3" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index fb45cf3da..259b241f7 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.19.2" + "version": "2.19.3" }, "snippets": [ { From d56ad12f197e9e379d2a4a0a38be108808985c23 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 9 Feb 2024 13:54:48 -0800 Subject: [PATCH 025/190] fix(diregapic): s/bazel/bazelisk/ in DIREGAPIC build GitHub action (#1064) Co-authored-by: Owl Bot Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Co-authored-by: Anthonios Partheniou --- google/cloud/pubsub_v1/publisher/client.py | 2 +- google/pubsub/__init__.py | 2 + google/pubsub_v1/__init__.py | 2 + .../services/publisher/async_client.py | 107 ++- google/pubsub_v1/services/publisher/client.py | 327 +++++++- .../services/publisher/transports/base.py | 8 +- .../services/publisher/transports/grpc.py | 5 +- .../publisher/transports/grpc_asyncio.py | 5 +- .../services/publisher/transports/rest.py | 6 +- .../services/schema_service/async_client.py | 107 ++- .../services/schema_service/client.py | 331 +++++++- .../schema_service/transports/base.py | 6 +- .../schema_service/transports/grpc.py | 2 +- .../schema_service/transports/grpc_asyncio.py | 2 +- .../schema_service/transports/rest.py | 6 +- .../services/subscriber/async_client.py | 155 +++- .../pubsub_v1/services/subscriber/client.py | 375 +++++++-- .../services/subscriber/transports/base.py | 8 +- .../services/subscriber/transports/grpc.py | 8 +- .../subscriber/transports/grpc_asyncio.py | 8 +- .../services/subscriber/transports/rest.py | 6 +- google/pubsub_v1/types/__init__.py | 2 + google/pubsub_v1/types/pubsub.py | 780 +++++++++++------- google/pubsub_v1/types/schema.py | 2 +- owlbot.py | 4 +- scripts/fixup_pubsub_v1_keywords.py | 2 +- testing/constraints-3.8.txt | 2 +- tests/unit/gapic/pubsub_v1/test_publisher.py | 482 ++++++++++- .../gapic/pubsub_v1/test_schema_service.py | 471 ++++++++++- tests/unit/gapic/pubsub_v1/test_subscriber.py | 457 +++++++++- .../publisher/test_publisher_client.py | 40 +- 31 files changed, 3112 insertions(+), 608 deletions(-) diff --git a/google/cloud/pubsub_v1/publisher/client.py b/google/cloud/pubsub_v1/publisher/client.py index 3e668533d..caf9fa180 100644 --- a/google/cloud/pubsub_v1/publisher/client.py +++ b/google/cloud/pubsub_v1/publisher/client.py @@ -399,7 +399,7 @@ def on_publish_done(future): transport = self._transport base_retry = transport._wrapped_methods[transport.publish]._retry retry = base_retry.with_deadline(2.0**32) - else: + elif retry is not None: retry = retry.with_deadline(2.0**32) # Delegate the publishing to the sequencer. diff --git a/google/pubsub/__init__.py b/google/pubsub/__init__.py index 453dca45f..ebcbb8271 100644 --- a/google/pubsub/__init__.py +++ b/google/pubsub/__init__.py @@ -41,6 +41,7 @@ from google.pubsub_v1.types.pubsub import GetSnapshotRequest from google.pubsub_v1.types.pubsub import GetSubscriptionRequest from google.pubsub_v1.types.pubsub import GetTopicRequest +from google.pubsub_v1.types.pubsub import IngestionDataSourceSettings from google.pubsub_v1.types.pubsub import ListSnapshotsRequest from google.pubsub_v1.types.pubsub import ListSnapshotsResponse from google.pubsub_v1.types.pubsub import ListSubscriptionsRequest @@ -112,6 +113,7 @@ "GetSnapshotRequest", "GetSubscriptionRequest", "GetTopicRequest", + "IngestionDataSourceSettings", "ListSnapshotsRequest", "ListSnapshotsResponse", "ListSubscriptionsRequest", diff --git a/google/pubsub_v1/__init__.py b/google/pubsub_v1/__init__.py index 08c6bc72a..731d03999 100644 --- a/google/pubsub_v1/__init__.py +++ b/google/pubsub_v1/__init__.py @@ -39,6 +39,7 @@ from .types.pubsub import GetSnapshotRequest from .types.pubsub import GetSubscriptionRequest from .types.pubsub import GetTopicRequest +from .types.pubsub import IngestionDataSourceSettings from .types.pubsub import ListSnapshotsRequest from .types.pubsub import ListSnapshotsResponse from .types.pubsub import ListSubscriptionsRequest @@ -113,6 +114,7 @@ "GetSnapshotRequest", "GetSubscriptionRequest", "GetTopicRequest", + "IngestionDataSourceSettings", "ListSchemaRevisionsRequest", "ListSchemaRevisionsResponse", "ListSchemasRequest", diff --git a/google/pubsub_v1/services/publisher/async_client.py b/google/pubsub_v1/services/publisher/async_client.py index ae632ea96..730d5c8a5 100644 --- a/google/pubsub_v1/services/publisher/async_client.py +++ b/google/pubsub_v1/services/publisher/async_client.py @@ -38,9 +38,9 @@ from google.oauth2 import service_account # type: ignore try: - OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault] + OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault, None] except AttributeError: # pragma: NO COVER - OptionalRetry = Union[retries.AsyncRetry, object] # type: ignore + OptionalRetry = Union[retries.AsyncRetry, object, None] # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -61,8 +61,12 @@ class PublisherAsyncClient: _client: PublisherClient + # Copy defaults from the synchronous client for use here. + # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = PublisherClient.DEFAULT_ENDPOINT DEFAULT_MTLS_ENDPOINT = PublisherClient.DEFAULT_MTLS_ENDPOINT + _DEFAULT_ENDPOINT_TEMPLATE = PublisherClient._DEFAULT_ENDPOINT_TEMPLATE + _DEFAULT_UNIVERSE = PublisherClient._DEFAULT_UNIVERSE schema_path = staticmethod(PublisherClient.schema_path) parse_schema_path = staticmethod(PublisherClient.parse_schema_path) @@ -167,6 +171,25 @@ def transport(self) -> PublisherTransport: """ return self._client.transport + @property + def api_endpoint(self): + """Return the API endpoint used by the client instance. + + Returns: + str: The API endpoint used by the client instance. + """ + return self._client._api_endpoint + + @property + def universe_domain(self) -> str: + """Return the universe domain used by the client instance. + + Returns: + str: The universe domain used + by the client instance. + """ + return self._client._universe_domain + get_transport_class = functools.partial( type(PublisherClient).get_transport_class, type(PublisherClient) ) @@ -179,7 +202,7 @@ def __init__( client_options: Optional[ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: - """Instantiates the publisher client. + """Instantiates the publisher async client. Args: credentials (Optional[google.auth.credentials.Credentials]): The @@ -190,23 +213,38 @@ def __init__( transport (Union[str, ~.PublisherTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (ClientOptions): Custom options for the client. It - won't take effect if a ``transport`` instance is provided. - (1) The ``api_endpoint`` property can be used to override the - default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT - environment variable can also be used to override the endpoint: + client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): + Custom options for the client. + + 1. The ``api_endpoint`` property can be used to override the + default endpoint provided by the client when ``transport`` is + not explicitly provided. Only if this property is not set and + ``transport`` was not explicitly provided, the endpoint is + determined by the GOOGLE_API_USE_MTLS_ENDPOINT environment + variable, which have one of the following values: "always" (always use the default mTLS endpoint), "never" (always - use the default regular endpoint) and "auto" (auto switch to the - default mTLS endpoint if client certificate is present, this is - the default value). However, the ``api_endpoint`` property takes - precedence if provided. - (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + use the default regular endpoint) and "auto" (auto-switch to the + default mTLS endpoint if client certificate is present; this is + the default value). + + 2. If the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable is "true", then the ``client_cert_source`` property can be used - to provide client certificate for mutual TLS transport. If + to provide a client certificate for mTLS transport. If not provided, the default SSL client certificate will be used if present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not set, no client certificate will be used. + 3. The ``universe_domain`` property can be used to override the + default "googleapis.com" universe. Note that ``api_endpoint`` + property still takes precedence; and ``universe_domain`` is + currently not supported for mTLS. + + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + Raises: google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport creation failed for any reason. @@ -324,6 +362,9 @@ async def sample_create_topic(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -345,7 +386,8 @@ async def update_topic( timeout: TimeoutType = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = (), ) -> pubsub.Topic: - r"""Updates an existing topic. Note that certain + r"""Updates an existing topic by updating the fields + specified in the update mask. Note that certain properties of a topic are not modifiable. .. code-block:: python @@ -452,6 +494,9 @@ async def sample_update_topic(): ), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -555,7 +600,7 @@ async def sample_publish(): default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, - multiplier=4.0, + multiplier=4, predicate=retries.if_exception_type( core_exceptions.Aborted, core_exceptions.Cancelled, @@ -577,6 +622,9 @@ async def sample_publish(): gapic_v1.routing_header.to_grpc_metadata((("topic", request.topic),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -688,6 +736,9 @@ async def sample_get_topic(): gapic_v1.routing_header.to_grpc_metadata((("topic", request.topic),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -804,6 +855,9 @@ async def sample_list_topics(): gapic_v1.routing_header.to_grpc_metadata((("project", request.project),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -931,6 +985,9 @@ async def sample_list_topic_subscriptions(): gapic_v1.routing_header.to_grpc_metadata((("topic", request.topic),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -1062,6 +1119,9 @@ async def sample_list_topic_snapshots(): gapic_v1.routing_header.to_grpc_metadata((("topic", request.topic),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -1178,6 +1238,9 @@ async def sample_delete_topic(): gapic_v1.routing_header.to_grpc_metadata((("topic", request.topic),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. await rpc( request, @@ -1271,6 +1334,9 @@ async def sample_detach_subscription(): ), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -1391,6 +1457,9 @@ async def set_iam_policy( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -1513,6 +1582,9 @@ async def get_iam_policy( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -1573,6 +1645,9 @@ async def test_iam_permissions( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, diff --git a/google/pubsub_v1/services/publisher/client.py b/google/pubsub_v1/services/publisher/client.py index 1a92362c5..37e7410f9 100644 --- a/google/pubsub_v1/services/publisher/client.py +++ b/google/pubsub_v1/services/publisher/client.py @@ -29,6 +29,7 @@ Union, cast, ) +import warnings from google.pubsub_v1 import gapic_version as package_version @@ -44,9 +45,9 @@ from google.oauth2 import service_account # type: ignore try: - OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault] + OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] except AttributeError: # pragma: NO COVER - OptionalRetry = Union[retries.Retry, object] # type: ignore + OptionalRetry = Union[retries.Retry, object, None] # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -133,6 +134,8 @@ def _get_default_mtls_endpoint(api_endpoint): return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. + # The scopes needed to make gRPC calls to all of the methods defined in # this service _DEFAULT_SCOPES = ( @@ -148,6 +151,9 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + _DEFAULT_ENDPOINT_TEMPLATE = "pubsub.{UNIVERSE_DOMAIN}" + _DEFAULT_UNIVERSE = "googleapis.com" + @classmethod def from_service_account_info(cls, info: dict, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -329,7 +335,7 @@ def parse_common_location_path(path: str) -> Dict[str, str]: def get_mtls_endpoint_and_cert_source( cls, client_options: Optional[client_options_lib.ClientOptions] = None ): - """Return the API endpoint and client cert source for mutual TLS. + """Deprecated. Return the API endpoint and client cert source for mutual TLS. The client cert source is determined in the following order: (1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the @@ -359,6 +365,11 @@ def get_mtls_endpoint_and_cert_source( Raises: google.auth.exceptions.MutualTLSChannelError: If any errors happen. """ + + warnings.warn( + "get_mtls_endpoint_and_cert_source is deprecated. Use the api_endpoint property instead.", + DeprecationWarning, + ) if client_options is None: client_options = client_options_lib.ClientOptions() use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") @@ -392,6 +403,178 @@ def get_mtls_endpoint_and_cert_source( return api_endpoint, client_cert_source + @staticmethod + def _read_environment_variables(): + """Returns the environment variables used by the client. + + Returns: + Tuple[bool, str, str]: returns the GOOGLE_API_USE_CLIENT_CERTIFICATE, + GOOGLE_API_USE_MTLS_ENDPOINT, and GOOGLE_CLOUD_UNIVERSE_DOMAIN environment variables. + + Raises: + ValueError: If GOOGLE_API_USE_CLIENT_CERTIFICATE is not + any of ["true", "false"]. + google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT + is not any of ["auto", "never", "always"]. + """ + use_client_cert = os.getenv( + "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false" + ).lower() + use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower() + universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN") + if use_client_cert not in ("true", "false"): + raise ValueError( + "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) + if use_mtls_endpoint not in ("auto", "never", "always"): + raise MutualTLSChannelError( + "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" + ) + return use_client_cert == "true", use_mtls_endpoint, universe_domain_env + + @staticmethod + def _get_client_cert_source(provided_cert_source, use_cert_flag): + """Return the client cert source to be used by the client. + + Args: + provided_cert_source (bytes): The client certificate source provided. + use_cert_flag (bool): A flag indicating whether to use the client certificate. + + Returns: + bytes or None: The client cert source to be used by the client. + """ + client_cert_source = None + if use_cert_flag: + if provided_cert_source: + client_cert_source = provided_cert_source + elif mtls.has_default_client_cert_source(): + client_cert_source = mtls.default_client_cert_source() + return client_cert_source + + @staticmethod + def _get_api_endpoint( + api_override, client_cert_source, universe_domain, use_mtls_endpoint + ): + """Return the API endpoint used by the client. + + Args: + api_override (str): The API endpoint override. If specified, this is always + the return value of this function and the other arguments are not used. + client_cert_source (bytes): The client certificate source used by the client. + universe_domain (str): The universe domain used by the client. + use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. + Possible values are "always", "auto", or "never". + + Returns: + str: The API endpoint to be used by the client. + """ + if api_override is not None: + api_endpoint = api_override + elif use_mtls_endpoint == "always" or ( + use_mtls_endpoint == "auto" and client_cert_source + ): + _default_universe = PublisherClient._DEFAULT_UNIVERSE + if universe_domain != _default_universe: + raise MutualTLSChannelError( + f"mTLS is not supported in any universe other than {_default_universe}." + ) + api_endpoint = PublisherClient.DEFAULT_MTLS_ENDPOINT + else: + api_endpoint = PublisherClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=universe_domain + ) + return api_endpoint + + @staticmethod + def _get_universe_domain( + client_universe_domain: Optional[str], universe_domain_env: Optional[str] + ) -> str: + """Return the universe domain used by the client. + + Args: + client_universe_domain (Optional[str]): The universe domain configured via the client options. + universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. + + Returns: + str: The universe domain to be used by the client. + + Raises: + ValueError: If the universe domain is an empty string. + """ + universe_domain = PublisherClient._DEFAULT_UNIVERSE + if client_universe_domain is not None: + universe_domain = client_universe_domain + elif universe_domain_env is not None: + universe_domain = universe_domain_env + if len(universe_domain.strip()) == 0: + raise ValueError("Universe Domain cannot be an empty string.") + return universe_domain + + @staticmethod + def _compare_universes( + client_universe: str, credentials: ga_credentials.Credentials + ) -> bool: + """Returns True iff the universe domains used by the client and credentials match. + + Args: + client_universe (str): The universe domain configured via the client options. + credentials (ga_credentials.Credentials): The credentials being used in the client. + + Returns: + bool: True iff client_universe matches the universe in credentials. + + Raises: + ValueError: when client_universe does not match the universe in credentials. + """ + + default_universe = PublisherClient._DEFAULT_UNIVERSE + credentials_universe = getattr(credentials, "universe_domain", default_universe) + + if client_universe != credentials_universe: + raise ValueError( + "The configured universe domain " + f"({client_universe}) does not match the universe domain " + f"found in the credentials ({credentials_universe}). " + "If you haven't configured the universe domain explicitly, " + f"`{default_universe}` is the default." + ) + return True + + def _validate_universe_domain(self): + """Validates client's and credentials' universe domains are consistent. + + Returns: + bool: True iff the configured universe domain is valid. + + Raises: + ValueError: If the configured universe domain is not valid. + """ + self._is_universe_domain_valid = ( + self._is_universe_domain_valid + or PublisherClient._compare_universes( + self.universe_domain, self.transport._credentials + ) + ) + return self._is_universe_domain_valid + + @property + def api_endpoint(self): + """Return the API endpoint used by the client instance. + + Returns: + str: The API endpoint used by the client instance. + """ + return self._api_endpoint + + @property + def universe_domain(self) -> str: + """Return the universe domain used by the client instance. + + Returns: + str: The universe domain used by the client instance. + """ + return self._universe_domain + def __init__( self, *, @@ -411,22 +594,32 @@ def __init__( transport (Union[str, PublisherTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): Custom options for the - client. It won't take effect if a ``transport`` instance is provided. - (1) The ``api_endpoint`` property can be used to override the - default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT - environment variable can also be used to override the endpoint: + client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): + Custom options for the client. + + 1. The ``api_endpoint`` property can be used to override the + default endpoint provided by the client when ``transport`` is + not explicitly provided. Only if this property is not set and + ``transport`` was not explicitly provided, the endpoint is + determined by the GOOGLE_API_USE_MTLS_ENDPOINT environment + variable, which have one of the following values: "always" (always use the default mTLS endpoint), "never" (always - use the default regular endpoint) and "auto" (auto switch to the - default mTLS endpoint if client certificate is present, this is - the default value). However, the ``api_endpoint`` property takes - precedence if provided. - (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + use the default regular endpoint) and "auto" (auto-switch to the + default mTLS endpoint if client certificate is present; this is + the default value). + + 2. If the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable is "true", then the ``client_cert_source`` property can be used - to provide client certificate for mutual TLS transport. If + to provide a client certificate for mTLS transport. If not provided, the default SSL client certificate will be used if present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not set, no client certificate will be used. + + 3. The ``universe_domain`` property can be used to override the + default "googleapis.com" universe. Note that the ``api_endpoint`` + property still takes precedence; and ``universe_domain`` is + currently not supported for mTLS. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): The client info used to send a user-agent string along with API requests. If ``None``, then default info will be used. @@ -437,17 +630,34 @@ def __init__( google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport creation failed for any reason. """ - if isinstance(client_options, dict): - client_options = client_options_lib.from_dict(client_options) - if client_options is None: - client_options = client_options_lib.ClientOptions() - client_options = cast(client_options_lib.ClientOptions, client_options) + self._client_options = client_options + if isinstance(self._client_options, dict): + self._client_options = client_options_lib.from_dict(self._client_options) + if self._client_options is None: + self._client_options = client_options_lib.ClientOptions() + self._client_options = cast( + client_options_lib.ClientOptions, self._client_options + ) - api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source( - client_options + universe_domain_opt = getattr(self._client_options, "universe_domain", None) + + ( + self._use_client_cert, + self._use_mtls_endpoint, + self._universe_domain_env, + ) = PublisherClient._read_environment_variables() + self._client_cert_source = PublisherClient._get_client_cert_source( + self._client_options.client_cert_source, self._use_client_cert + ) + self._universe_domain = PublisherClient._get_universe_domain( + universe_domain_opt, self._universe_domain_env ) + self._api_endpoint = None # updated below, depending on `transport` - api_key_value = getattr(client_options, "api_key", None) + # Initialize the universe domain validation. + self._is_universe_domain_valid = False + + api_key_value = getattr(self._client_options, "api_key", None) if api_key_value and credentials: raise ValueError( "client_options.api_key and credentials are mutually exclusive" @@ -456,20 +666,30 @@ def __init__( # Save or instantiate the transport. # Ordinarily, we provide the transport, but allowing a custom transport # instance provides an extensibility point for unusual situations. - if isinstance(transport, PublisherTransport): + transport_provided = isinstance(transport, PublisherTransport) + if transport_provided: # transport is a PublisherTransport instance. - if credentials or client_options.credentials_file or api_key_value: + if credentials or self._client_options.credentials_file or api_key_value: raise ValueError( "When providing a transport instance, " "provide its credentials directly." ) - if client_options.scopes: + if self._client_options.scopes: raise ValueError( "When providing a transport instance, provide its scopes " "directly." ) - self._transport = transport - else: + self._transport = cast(PublisherTransport, transport) + self._api_endpoint = self._transport.host + + self._api_endpoint = self._api_endpoint or PublisherClient._get_api_endpoint( + self._client_options.api_endpoint, + self._client_cert_source, + self._universe_domain, + self._use_mtls_endpoint, + ) + + if not transport_provided: import google.auth._default # type: ignore if api_key_value and hasattr( @@ -479,7 +699,7 @@ def __init__( api_key_value ) - Transport = type(self).get_transport_class(transport) + Transport = type(self).get_transport_class(cast(str, transport)) emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") if emulator_host: @@ -491,14 +711,14 @@ def __init__( self._transport = Transport( credentials=credentials, - credentials_file=client_options.credentials_file, - host=api_endpoint, - scopes=client_options.scopes, - client_cert_source_for_mtls=client_cert_source_func, - quota_project_id=client_options.quota_project_id, + credentials_file=self._client_options.credentials_file, + host=self._api_endpoint, + scopes=self._client_options.scopes, + client_cert_source_for_mtls=self._client_cert_source, + quota_project_id=self._client_options.quota_project_id, client_info=client_info, always_use_jwt_access=True, - api_audience=client_options.api_audience, + api_audience=self._client_options.api_audience, ) def create_topic( @@ -598,6 +818,9 @@ def sample_create_topic(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -619,7 +842,8 @@ def update_topic( timeout: TimeoutType = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = (), ) -> pubsub.Topic: - r"""Updates an existing topic. Note that certain + r"""Updates an existing topic by updating the fields + specified in the update mask. Note that certain properties of a topic are not modifiable. .. code-block:: python @@ -717,6 +941,9 @@ def sample_update_topic(): ), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -827,6 +1054,9 @@ def sample_publish(): gapic_v1.routing_header.to_grpc_metadata((("topic", request.topic),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -927,6 +1157,9 @@ def sample_get_topic(): gapic_v1.routing_header.to_grpc_metadata((("topic", request.topic),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1032,6 +1265,9 @@ def sample_list_topics(): gapic_v1.routing_header.to_grpc_metadata((("project", request.project),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1148,6 +1384,9 @@ def sample_list_topic_subscriptions(): gapic_v1.routing_header.to_grpc_metadata((("topic", request.topic),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1268,6 +1507,9 @@ def sample_list_topic_snapshots(): gapic_v1.routing_header.to_grpc_metadata((("topic", request.topic),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1375,6 +1617,9 @@ def sample_delete_topic(): gapic_v1.routing_header.to_grpc_metadata((("topic", request.topic),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. rpc( request, @@ -1460,6 +1705,9 @@ def sample_detach_subscription(): ), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1594,6 +1842,9 @@ def set_iam_policy( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1716,6 +1967,9 @@ def get_iam_policy( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1776,6 +2030,9 @@ def test_iam_permissions( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, diff --git a/google/pubsub_v1/services/publisher/transports/base.py b/google/pubsub_v1/services/publisher/transports/base.py index e8caa080b..6c92dd8f9 100644 --- a/google/pubsub_v1/services/publisher/transports/base.py +++ b/google/pubsub_v1/services/publisher/transports/base.py @@ -63,7 +63,7 @@ def __init__( Args: host (Optional[str]): - The hostname to connect to. + The hostname to connect to (default: 'pubsub.googleapis.com'). credentials (Optional[google.auth.credentials.Credentials]): The authorization credentials to attach to requests. These credentials identify the application to the service; if none @@ -126,6 +126,10 @@ def __init__( host += ":443" self._host = host + @property + def host(self): + return self._host + def _prep_wrapped_messages(self, client_info): # Precompute the wrapped methods. self._wrapped_methods = { @@ -162,7 +166,7 @@ def _prep_wrapped_messages(self, client_info): default_retry=retries.Retry( initial=0.1, maximum=60.0, - multiplier=4.0, + multiplier=4, predicate=retries.if_exception_type( core_exceptions.Aborted, core_exceptions.Cancelled, diff --git a/google/pubsub_v1/services/publisher/transports/grpc.py b/google/pubsub_v1/services/publisher/transports/grpc.py index 268e687f0..ed6410fd4 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc.py +++ b/google/pubsub_v1/services/publisher/transports/grpc.py @@ -68,7 +68,7 @@ def __init__( Args: host (Optional[str]): - The hostname to connect to. + The hostname to connect to (default: 'pubsub.googleapis.com'). credentials (Optional[google.auth.credentials.Credentials]): The authorization credentials to attach to requests. These credentials identify the application to the service; if none @@ -265,7 +265,8 @@ def create_topic(self) -> Callable[[pubsub.Topic], pubsub.Topic]: def update_topic(self) -> Callable[[pubsub.UpdateTopicRequest], pubsub.Topic]: r"""Return a callable for the update topic method over gRPC. - Updates an existing topic. Note that certain + Updates an existing topic by updating the fields + specified in the update mask. Note that certain properties of a topic are not modifiable. Returns: diff --git a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py index 8d971fa32..c620cd4ae 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py @@ -113,7 +113,7 @@ def __init__( Args: host (Optional[str]): - The hostname to connect to. + The hostname to connect to (default: 'pubsub.googleapis.com'). credentials (Optional[google.auth.credentials.Credentials]): The authorization credentials to attach to requests. These credentials identify the application to the service; if none @@ -270,7 +270,8 @@ def update_topic( ) -> Callable[[pubsub.UpdateTopicRequest], Awaitable[pubsub.Topic]]: r"""Return a callable for the update topic method over gRPC. - Updates an existing topic. Note that certain + Updates an existing topic by updating the fields + specified in the update mask. Note that certain properties of a topic are not modifiable. Returns: diff --git a/google/pubsub_v1/services/publisher/transports/rest.py b/google/pubsub_v1/services/publisher/transports/rest.py index de7215153..d450cb6eb 100644 --- a/google/pubsub_v1/services/publisher/transports/rest.py +++ b/google/pubsub_v1/services/publisher/transports/rest.py @@ -36,9 +36,9 @@ import warnings try: - OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault] + OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] except AttributeError: # pragma: NO COVER - OptionalRetry = Union[retries.Retry, object] # type: ignore + OptionalRetry = Union[retries.Retry, object, None] # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore @@ -427,7 +427,7 @@ def __init__( Args: host (Optional[str]): - The hostname to connect to. + The hostname to connect to (default: 'pubsub.googleapis.com'). credentials (Optional[google.auth.credentials.Credentials]): The authorization credentials to attach to requests. These credentials identify the application to the service; if none diff --git a/google/pubsub_v1/services/schema_service/async_client.py b/google/pubsub_v1/services/schema_service/async_client.py index 2d4b5d77b..44a4916e6 100644 --- a/google/pubsub_v1/services/schema_service/async_client.py +++ b/google/pubsub_v1/services/schema_service/async_client.py @@ -38,9 +38,9 @@ from google.oauth2 import service_account # type: ignore try: - OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault] + OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault, None] except AttributeError: # pragma: NO COVER - OptionalRetry = Union[retries.AsyncRetry, object] # type: ignore + OptionalRetry = Union[retries.AsyncRetry, object, None] # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -58,8 +58,12 @@ class SchemaServiceAsyncClient: _client: SchemaServiceClient + # Copy defaults from the synchronous client for use here. + # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = SchemaServiceClient.DEFAULT_ENDPOINT DEFAULT_MTLS_ENDPOINT = SchemaServiceClient.DEFAULT_MTLS_ENDPOINT + _DEFAULT_ENDPOINT_TEMPLATE = SchemaServiceClient._DEFAULT_ENDPOINT_TEMPLATE + _DEFAULT_UNIVERSE = SchemaServiceClient._DEFAULT_UNIVERSE schema_path = staticmethod(SchemaServiceClient.schema_path) parse_schema_path = staticmethod(SchemaServiceClient.parse_schema_path) @@ -166,6 +170,25 @@ def transport(self) -> SchemaServiceTransport: """ return self._client.transport + @property + def api_endpoint(self): + """Return the API endpoint used by the client instance. + + Returns: + str: The API endpoint used by the client instance. + """ + return self._client._api_endpoint + + @property + def universe_domain(self) -> str: + """Return the universe domain used by the client instance. + + Returns: + str: The universe domain used + by the client instance. + """ + return self._client._universe_domain + get_transport_class = functools.partial( type(SchemaServiceClient).get_transport_class, type(SchemaServiceClient) ) @@ -178,7 +201,7 @@ def __init__( client_options: Optional[ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: - """Instantiates the schema service client. + """Instantiates the schema service async client. Args: credentials (Optional[google.auth.credentials.Credentials]): The @@ -189,23 +212,38 @@ def __init__( transport (Union[str, ~.SchemaServiceTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (ClientOptions): Custom options for the client. It - won't take effect if a ``transport`` instance is provided. - (1) The ``api_endpoint`` property can be used to override the - default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT - environment variable can also be used to override the endpoint: + client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): + Custom options for the client. + + 1. The ``api_endpoint`` property can be used to override the + default endpoint provided by the client when ``transport`` is + not explicitly provided. Only if this property is not set and + ``transport`` was not explicitly provided, the endpoint is + determined by the GOOGLE_API_USE_MTLS_ENDPOINT environment + variable, which have one of the following values: "always" (always use the default mTLS endpoint), "never" (always - use the default regular endpoint) and "auto" (auto switch to the - default mTLS endpoint if client certificate is present, this is - the default value). However, the ``api_endpoint`` property takes - precedence if provided. - (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + use the default regular endpoint) and "auto" (auto-switch to the + default mTLS endpoint if client certificate is present; this is + the default value). + + 2. If the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable is "true", then the ``client_cert_source`` property can be used - to provide client certificate for mutual TLS transport. If + to provide a client certificate for mTLS transport. If not provided, the default SSL client certificate will be used if present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not set, no client certificate will be used. + 3. The ``universe_domain`` property can be used to override the + default "googleapis.com" universe. Note that ``api_endpoint`` + property still takes precedence; and ``universe_domain`` is + currently not supported for mTLS. + + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + Raises: google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport creation failed for any reason. @@ -285,7 +323,7 @@ async def sample_create_schema(): final component of the schema's resource name. See - https://cloud.google.com/pubsub/docs/admin#resource_names + https://cloud.google.com/pubsub/docs/pubsub-basics#resource_names for resource name constraints. This corresponds to the ``schema_id`` field @@ -345,6 +383,9 @@ async def sample_create_schema(): gapic_v1.routing_header.to_grpc_metadata((("parent", request.parent),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -453,6 +494,9 @@ async def sample_get_schema(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -566,6 +610,9 @@ async def sample_list_schemas(): gapic_v1.routing_header.to_grpc_metadata((("parent", request.parent),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -688,6 +735,9 @@ async def sample_list_schema_revisions(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -819,6 +869,9 @@ async def sample_commit_schema(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -942,6 +995,9 @@ async def sample_rollback_schema(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -1063,6 +1119,9 @@ async def sample_delete_schema_revision(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -1164,6 +1223,9 @@ async def sample_delete_schema(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. await rpc( request, @@ -1285,6 +1347,9 @@ async def sample_validate_schema(): gapic_v1.routing_header.to_grpc_metadata((("parent", request.parent),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -1374,6 +1439,9 @@ async def sample_validate_message(): gapic_v1.routing_header.to_grpc_metadata((("parent", request.parent),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -1493,6 +1561,9 @@ async def set_iam_policy( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -1614,6 +1685,9 @@ async def get_iam_policy( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -1673,6 +1747,9 @@ async def test_iam_permissions( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, diff --git a/google/pubsub_v1/services/schema_service/client.py b/google/pubsub_v1/services/schema_service/client.py index 3106a3292..d869e4dec 100644 --- a/google/pubsub_v1/services/schema_service/client.py +++ b/google/pubsub_v1/services/schema_service/client.py @@ -29,6 +29,7 @@ Union, cast, ) +import warnings from google.pubsub_v1 import gapic_version as package_version @@ -43,9 +44,9 @@ from google.oauth2 import service_account # type: ignore try: - OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault] + OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] except AttributeError: # pragma: NO COVER - OptionalRetry = Union[retries.Retry, object] # type: ignore + OptionalRetry = Union[retries.Retry, object, None] # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -129,11 +130,15 @@ def _get_default_mtls_endpoint(api_endpoint): return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = "pubsub.googleapis.com" DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore DEFAULT_ENDPOINT ) + _DEFAULT_ENDPOINT_TEMPLATE = "pubsub.{UNIVERSE_DOMAIN}" + _DEFAULT_UNIVERSE = "googleapis.com" + @classmethod def from_service_account_info(cls, info: dict, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -279,7 +284,7 @@ def parse_common_location_path(path: str) -> Dict[str, str]: def get_mtls_endpoint_and_cert_source( cls, client_options: Optional[client_options_lib.ClientOptions] = None ): - """Return the API endpoint and client cert source for mutual TLS. + """Deprecated. Return the API endpoint and client cert source for mutual TLS. The client cert source is determined in the following order: (1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the @@ -309,6 +314,11 @@ def get_mtls_endpoint_and_cert_source( Raises: google.auth.exceptions.MutualTLSChannelError: If any errors happen. """ + + warnings.warn( + "get_mtls_endpoint_and_cert_source is deprecated. Use the api_endpoint property instead.", + DeprecationWarning, + ) if client_options is None: client_options = client_options_lib.ClientOptions() use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") @@ -342,6 +352,178 @@ def get_mtls_endpoint_and_cert_source( return api_endpoint, client_cert_source + @staticmethod + def _read_environment_variables(): + """Returns the environment variables used by the client. + + Returns: + Tuple[bool, str, str]: returns the GOOGLE_API_USE_CLIENT_CERTIFICATE, + GOOGLE_API_USE_MTLS_ENDPOINT, and GOOGLE_CLOUD_UNIVERSE_DOMAIN environment variables. + + Raises: + ValueError: If GOOGLE_API_USE_CLIENT_CERTIFICATE is not + any of ["true", "false"]. + google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT + is not any of ["auto", "never", "always"]. + """ + use_client_cert = os.getenv( + "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false" + ).lower() + use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower() + universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN") + if use_client_cert not in ("true", "false"): + raise ValueError( + "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) + if use_mtls_endpoint not in ("auto", "never", "always"): + raise MutualTLSChannelError( + "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" + ) + return use_client_cert == "true", use_mtls_endpoint, universe_domain_env + + @staticmethod + def _get_client_cert_source(provided_cert_source, use_cert_flag): + """Return the client cert source to be used by the client. + + Args: + provided_cert_source (bytes): The client certificate source provided. + use_cert_flag (bool): A flag indicating whether to use the client certificate. + + Returns: + bytes or None: The client cert source to be used by the client. + """ + client_cert_source = None + if use_cert_flag: + if provided_cert_source: + client_cert_source = provided_cert_source + elif mtls.has_default_client_cert_source(): + client_cert_source = mtls.default_client_cert_source() + return client_cert_source + + @staticmethod + def _get_api_endpoint( + api_override, client_cert_source, universe_domain, use_mtls_endpoint + ): + """Return the API endpoint used by the client. + + Args: + api_override (str): The API endpoint override. If specified, this is always + the return value of this function and the other arguments are not used. + client_cert_source (bytes): The client certificate source used by the client. + universe_domain (str): The universe domain used by the client. + use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. + Possible values are "always", "auto", or "never". + + Returns: + str: The API endpoint to be used by the client. + """ + if api_override is not None: + api_endpoint = api_override + elif use_mtls_endpoint == "always" or ( + use_mtls_endpoint == "auto" and client_cert_source + ): + _default_universe = SchemaServiceClient._DEFAULT_UNIVERSE + if universe_domain != _default_universe: + raise MutualTLSChannelError( + f"mTLS is not supported in any universe other than {_default_universe}." + ) + api_endpoint = SchemaServiceClient.DEFAULT_MTLS_ENDPOINT + else: + api_endpoint = SchemaServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=universe_domain + ) + return api_endpoint + + @staticmethod + def _get_universe_domain( + client_universe_domain: Optional[str], universe_domain_env: Optional[str] + ) -> str: + """Return the universe domain used by the client. + + Args: + client_universe_domain (Optional[str]): The universe domain configured via the client options. + universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. + + Returns: + str: The universe domain to be used by the client. + + Raises: + ValueError: If the universe domain is an empty string. + """ + universe_domain = SchemaServiceClient._DEFAULT_UNIVERSE + if client_universe_domain is not None: + universe_domain = client_universe_domain + elif universe_domain_env is not None: + universe_domain = universe_domain_env + if len(universe_domain.strip()) == 0: + raise ValueError("Universe Domain cannot be an empty string.") + return universe_domain + + @staticmethod + def _compare_universes( + client_universe: str, credentials: ga_credentials.Credentials + ) -> bool: + """Returns True iff the universe domains used by the client and credentials match. + + Args: + client_universe (str): The universe domain configured via the client options. + credentials (ga_credentials.Credentials): The credentials being used in the client. + + Returns: + bool: True iff client_universe matches the universe in credentials. + + Raises: + ValueError: when client_universe does not match the universe in credentials. + """ + + default_universe = SchemaServiceClient._DEFAULT_UNIVERSE + credentials_universe = getattr(credentials, "universe_domain", default_universe) + + if client_universe != credentials_universe: + raise ValueError( + "The configured universe domain " + f"({client_universe}) does not match the universe domain " + f"found in the credentials ({credentials_universe}). " + "If you haven't configured the universe domain explicitly, " + f"`{default_universe}` is the default." + ) + return True + + def _validate_universe_domain(self): + """Validates client's and credentials' universe domains are consistent. + + Returns: + bool: True iff the configured universe domain is valid. + + Raises: + ValueError: If the configured universe domain is not valid. + """ + self._is_universe_domain_valid = ( + self._is_universe_domain_valid + or SchemaServiceClient._compare_universes( + self.universe_domain, self.transport._credentials + ) + ) + return self._is_universe_domain_valid + + @property + def api_endpoint(self): + """Return the API endpoint used by the client instance. + + Returns: + str: The API endpoint used by the client instance. + """ + return self._api_endpoint + + @property + def universe_domain(self) -> str: + """Return the universe domain used by the client instance. + + Returns: + str: The universe domain used by the client instance. + """ + return self._universe_domain + def __init__( self, *, @@ -361,22 +543,32 @@ def __init__( transport (Union[str, SchemaServiceTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): Custom options for the - client. It won't take effect if a ``transport`` instance is provided. - (1) The ``api_endpoint`` property can be used to override the - default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT - environment variable can also be used to override the endpoint: + client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): + Custom options for the client. + + 1. The ``api_endpoint`` property can be used to override the + default endpoint provided by the client when ``transport`` is + not explicitly provided. Only if this property is not set and + ``transport`` was not explicitly provided, the endpoint is + determined by the GOOGLE_API_USE_MTLS_ENDPOINT environment + variable, which have one of the following values: "always" (always use the default mTLS endpoint), "never" (always - use the default regular endpoint) and "auto" (auto switch to the - default mTLS endpoint if client certificate is present, this is - the default value). However, the ``api_endpoint`` property takes - precedence if provided. - (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + use the default regular endpoint) and "auto" (auto-switch to the + default mTLS endpoint if client certificate is present; this is + the default value). + + 2. If the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable is "true", then the ``client_cert_source`` property can be used - to provide client certificate for mutual TLS transport. If + to provide a client certificate for mTLS transport. If not provided, the default SSL client certificate will be used if present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not set, no client certificate will be used. + + 3. The ``universe_domain`` property can be used to override the + default "googleapis.com" universe. Note that the ``api_endpoint`` + property still takes precedence; and ``universe_domain`` is + currently not supported for mTLS. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): The client info used to send a user-agent string along with API requests. If ``None``, then default info will be used. @@ -387,17 +579,34 @@ def __init__( google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport creation failed for any reason. """ - if isinstance(client_options, dict): - client_options = client_options_lib.from_dict(client_options) - if client_options is None: - client_options = client_options_lib.ClientOptions() - client_options = cast(client_options_lib.ClientOptions, client_options) + self._client_options = client_options + if isinstance(self._client_options, dict): + self._client_options = client_options_lib.from_dict(self._client_options) + if self._client_options is None: + self._client_options = client_options_lib.ClientOptions() + self._client_options = cast( + client_options_lib.ClientOptions, self._client_options + ) - api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source( - client_options + universe_domain_opt = getattr(self._client_options, "universe_domain", None) + + ( + self._use_client_cert, + self._use_mtls_endpoint, + self._universe_domain_env, + ) = SchemaServiceClient._read_environment_variables() + self._client_cert_source = SchemaServiceClient._get_client_cert_source( + self._client_options.client_cert_source, self._use_client_cert + ) + self._universe_domain = SchemaServiceClient._get_universe_domain( + universe_domain_opt, self._universe_domain_env ) + self._api_endpoint = None # updated below, depending on `transport` - api_key_value = getattr(client_options, "api_key", None) + # Initialize the universe domain validation. + self._is_universe_domain_valid = False + + api_key_value = getattr(self._client_options, "api_key", None) if api_key_value and credentials: raise ValueError( "client_options.api_key and credentials are mutually exclusive" @@ -406,20 +615,33 @@ def __init__( # Save or instantiate the transport. # Ordinarily, we provide the transport, but allowing a custom transport # instance provides an extensibility point for unusual situations. - if isinstance(transport, SchemaServiceTransport): + transport_provided = isinstance(transport, SchemaServiceTransport) + if transport_provided: # transport is a SchemaServiceTransport instance. - if credentials or client_options.credentials_file or api_key_value: + if credentials or self._client_options.credentials_file or api_key_value: raise ValueError( "When providing a transport instance, " "provide its credentials directly." ) - if client_options.scopes: + if self._client_options.scopes: raise ValueError( "When providing a transport instance, provide its scopes " "directly." ) - self._transport = transport - else: + self._transport = cast(SchemaServiceTransport, transport) + self._api_endpoint = self._transport.host + + self._api_endpoint = ( + self._api_endpoint + or SchemaServiceClient._get_api_endpoint( + self._client_options.api_endpoint, + self._client_cert_source, + self._universe_domain, + self._use_mtls_endpoint, + ) + ) + + if not transport_provided: import google.auth._default # type: ignore if api_key_value and hasattr( @@ -429,7 +651,7 @@ def __init__( api_key_value ) - Transport = type(self).get_transport_class(transport) + Transport = type(self).get_transport_class(cast(str, transport)) emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") if emulator_host: @@ -441,14 +663,14 @@ def __init__( self._transport = Transport( credentials=credentials, - credentials_file=client_options.credentials_file, - host=api_endpoint, - scopes=client_options.scopes, - client_cert_source_for_mtls=client_cert_source_func, - quota_project_id=client_options.quota_project_id, + credentials_file=self._client_options.credentials_file, + host=self._api_endpoint, + scopes=self._client_options.scopes, + client_cert_source_for_mtls=self._client_cert_source, + quota_project_id=self._client_options.quota_project_id, client_info=client_info, always_use_jwt_access=True, - api_audience=client_options.api_audience, + api_audience=self._client_options.api_audience, ) def create_schema( @@ -519,7 +741,7 @@ def sample_create_schema(): final component of the schema's resource name. See - https://cloud.google.com/pubsub/docs/admin#resource_names + https://cloud.google.com/pubsub/docs/pubsub-basics#resource_names for resource name constraints. This corresponds to the ``schema_id`` field @@ -570,6 +792,9 @@ def sample_create_schema(): gapic_v1.routing_header.to_grpc_metadata((("parent", request.parent),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -669,6 +894,9 @@ def sample_get_schema(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -773,6 +1001,9 @@ def sample_list_schemas(): gapic_v1.routing_header.to_grpc_metadata((("parent", request.parent),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -886,6 +1117,9 @@ def sample_list_schema_revisions(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1008,6 +1242,9 @@ def sample_commit_schema(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1122,6 +1359,9 @@ def sample_rollback_schema(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1234,6 +1474,9 @@ def sample_delete_schema_revision(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1326,6 +1569,9 @@ def sample_delete_schema(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. rpc( request, @@ -1438,6 +1684,9 @@ def sample_validate_schema(): gapic_v1.routing_header.to_grpc_metadata((("parent", request.parent),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1519,6 +1768,9 @@ def sample_validate_message(): gapic_v1.routing_header.to_grpc_metadata((("parent", request.parent),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1652,6 +1904,9 @@ def set_iam_policy( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1773,6 +2028,9 @@ def get_iam_policy( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1832,6 +2090,9 @@ def test_iam_permissions( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, diff --git a/google/pubsub_v1/services/schema_service/transports/base.py b/google/pubsub_v1/services/schema_service/transports/base.py index 08b370cf7..39151e397 100644 --- a/google/pubsub_v1/services/schema_service/transports/base.py +++ b/google/pubsub_v1/services/schema_service/transports/base.py @@ -64,7 +64,7 @@ def __init__( Args: host (Optional[str]): - The hostname to connect to. + The hostname to connect to (default: 'pubsub.googleapis.com'). credentials (Optional[google.auth.credentials.Credentials]): The authorization credentials to attach to requests. These credentials identify the application to the service; if none @@ -127,6 +127,10 @@ def __init__( host += ":443" self._host = host + @property + def host(self): + return self._host + def _prep_wrapped_messages(self, client_info): # Precompute the wrapped methods. self._wrapped_methods = { diff --git a/google/pubsub_v1/services/schema_service/transports/grpc.py b/google/pubsub_v1/services/schema_service/transports/grpc.py index c14a1d5f4..66b382bfb 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc.py @@ -68,7 +68,7 @@ def __init__( Args: host (Optional[str]): - The hostname to connect to. + The hostname to connect to (default: 'pubsub.googleapis.com'). credentials (Optional[google.auth.credentials.Credentials]): The authorization credentials to attach to requests. These credentials identify the application to the service; if none diff --git a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py index 5b435aff6..94a872b56 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py @@ -113,7 +113,7 @@ def __init__( Args: host (Optional[str]): - The hostname to connect to. + The hostname to connect to (default: 'pubsub.googleapis.com'). credentials (Optional[google.auth.credentials.Credentials]): The authorization credentials to attach to requests. These credentials identify the application to the service; if none diff --git a/google/pubsub_v1/services/schema_service/transports/rest.py b/google/pubsub_v1/services/schema_service/transports/rest.py index 0586bbf9d..5e5d7d478 100644 --- a/google/pubsub_v1/services/schema_service/transports/rest.py +++ b/google/pubsub_v1/services/schema_service/transports/rest.py @@ -36,9 +36,9 @@ import warnings try: - OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault] + OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] except AttributeError: # pragma: NO COVER - OptionalRetry = Union[retries.Retry, object] # type: ignore + OptionalRetry = Union[retries.Retry, object, None] # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore @@ -463,7 +463,7 @@ def __init__( Args: host (Optional[str]): - The hostname to connect to. + The hostname to connect to (default: 'pubsub.googleapis.com'). credentials (Optional[google.auth.credentials.Credentials]): The authorization credentials to attach to requests. These credentials identify the application to the service; if none diff --git a/google/pubsub_v1/services/subscriber/async_client.py b/google/pubsub_v1/services/subscriber/async_client.py index 13f30bd89..78ad7c9a2 100644 --- a/google/pubsub_v1/services/subscriber/async_client.py +++ b/google/pubsub_v1/services/subscriber/async_client.py @@ -42,9 +42,9 @@ from google.oauth2 import service_account # type: ignore try: - OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault] + OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault, None] except AttributeError: # pragma: NO COVER - OptionalRetry = Union[retries.AsyncRetry, object] # type: ignore + OptionalRetry = Union[retries.AsyncRetry, object, None] # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -67,8 +67,12 @@ class SubscriberAsyncClient: _client: SubscriberClient + # Copy defaults from the synchronous client for use here. + # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. DEFAULT_ENDPOINT = SubscriberClient.DEFAULT_ENDPOINT DEFAULT_MTLS_ENDPOINT = SubscriberClient.DEFAULT_MTLS_ENDPOINT + _DEFAULT_ENDPOINT_TEMPLATE = SubscriberClient._DEFAULT_ENDPOINT_TEMPLATE + _DEFAULT_UNIVERSE = SubscriberClient._DEFAULT_UNIVERSE snapshot_path = staticmethod(SubscriberClient.snapshot_path) parse_snapshot_path = staticmethod(SubscriberClient.parse_snapshot_path) @@ -173,6 +177,25 @@ def transport(self) -> SubscriberTransport: """ return self._client.transport + @property + def api_endpoint(self): + """Return the API endpoint used by the client instance. + + Returns: + str: The API endpoint used by the client instance. + """ + return self._client._api_endpoint + + @property + def universe_domain(self) -> str: + """Return the universe domain used by the client instance. + + Returns: + str: The universe domain used + by the client instance. + """ + return self._client._universe_domain + get_transport_class = functools.partial( type(SubscriberClient).get_transport_class, type(SubscriberClient) ) @@ -185,7 +208,7 @@ def __init__( client_options: Optional[ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: - """Instantiates the subscriber client. + """Instantiates the subscriber async client. Args: credentials (Optional[google.auth.credentials.Credentials]): The @@ -196,23 +219,38 @@ def __init__( transport (Union[str, ~.SubscriberTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (ClientOptions): Custom options for the client. It - won't take effect if a ``transport`` instance is provided. - (1) The ``api_endpoint`` property can be used to override the - default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT - environment variable can also be used to override the endpoint: + client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): + Custom options for the client. + + 1. The ``api_endpoint`` property can be used to override the + default endpoint provided by the client when ``transport`` is + not explicitly provided. Only if this property is not set and + ``transport`` was not explicitly provided, the endpoint is + determined by the GOOGLE_API_USE_MTLS_ENDPOINT environment + variable, which have one of the following values: "always" (always use the default mTLS endpoint), "never" (always - use the default regular endpoint) and "auto" (auto switch to the - default mTLS endpoint if client certificate is present, this is - the default value). However, the ``api_endpoint`` property takes - precedence if provided. - (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + use the default regular endpoint) and "auto" (auto-switch to the + default mTLS endpoint if client certificate is present; this is + the default value). + + 2. If the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable is "true", then the ``client_cert_source`` property can be used - to provide client certificate for mutual TLS transport. If + to provide a client certificate for mTLS transport. If not provided, the default SSL client certificate will be used if present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not set, no client certificate will be used. + 3. The ``universe_domain`` property can be used to override the + default "googleapis.com" universe. Note that ``api_endpoint`` + property still takes precedence; and ``universe_domain`` is + currently not supported for mTLS. + + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + Raises: google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport creation failed for any reason. @@ -308,21 +346,21 @@ async def sample_create_subscription(): on the ``request`` instance; if ``request`` is provided, this should not be set. push_config (:class:`google.pubsub_v1.types.PushConfig`): - If push delivery is used with this - subscription, this field is used to - configure it. + Optional. If push delivery is used + with this subscription, this field is + used to configure it. This corresponds to the ``push_config`` field on the ``request`` instance; if ``request`` is provided, this should not be set. ack_deadline_seconds (:class:`int`): - The approximate amount of time (on a best-effort basis) - Pub/Sub waits for the subscriber to acknowledge receipt - before resending the message. In the interval after the - message is delivered and before it is acknowledged, it - is considered to be *outstanding*. During that time - period, the message will not be redelivered (on a - best-effort basis). + Optional. The approximate amount of time (on a + best-effort basis) Pub/Sub waits for the subscriber to + acknowledge receipt before resending the message. In the + interval after the message is delivered and before it is + acknowledged, it is considered to be *outstanding*. + During that time period, the message will not be + redelivered (on a best-effort basis). For pull subscriptions, this value is used as the initial value for the ack deadline. To override this @@ -406,6 +444,9 @@ async def sample_create_subscription(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -523,6 +564,9 @@ async def sample_get_subscription(): ), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -544,7 +588,8 @@ async def update_subscription( timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = (), ) -> pubsub.Subscription: - r"""Updates an existing subscription. Note that certain + r"""Updates an existing subscription by updating the + fields specified in the update mask. Note that certain properties of a subscription, such as its topic, are not modifiable. @@ -655,6 +700,9 @@ async def sample_update_subscription(): ), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -770,6 +818,9 @@ async def sample_list_subscriptions(): gapic_v1.routing_header.to_grpc_metadata((("project", request.project),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -888,6 +939,9 @@ async def sample_delete_subscription(): ), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. await rpc( request, @@ -965,8 +1019,8 @@ async def sample_modify_ack_deadline(): client. This typically results in an increase in the rate of message redeliveries (that is, duplicates). The minimum deadline you can specify is 0 seconds. The - maximum deadline you can specify is 600 seconds (10 - minutes). + maximum deadline you can specify in a single request is + 600 seconds (10 minutes). This corresponds to the ``ack_deadline_seconds`` field on the ``request`` instance; if ``request`` is provided, this @@ -1023,6 +1077,9 @@ async def sample_modify_ack_deadline(): ), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. await rpc( request, @@ -1143,6 +1200,9 @@ async def sample_acknowledge(): ), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. await rpc( request, @@ -1291,6 +1351,9 @@ async def sample_pull(): ), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -1383,7 +1446,7 @@ def request_generator(): default_retry=retries.AsyncRetry( initial=0.1, maximum=60.0, - multiplier=4.0, + multiplier=4, predicate=retries.if_exception_type( core_exceptions.Aborted, core_exceptions.DeadlineExceeded, @@ -1397,6 +1460,9 @@ def request_generator(): client_info=DEFAULT_CLIENT_INFO, ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = rpc( requests, @@ -1522,6 +1588,9 @@ async def sample_modify_push_config(): ), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. await rpc( request, @@ -1640,6 +1709,9 @@ async def sample_get_snapshot(): gapic_v1.routing_header.to_grpc_metadata((("snapshot", request.snapshot),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -1759,6 +1831,9 @@ async def sample_list_snapshots(): gapic_v1.routing_header.to_grpc_metadata((("project", request.project),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -1925,6 +2000,9 @@ async def sample_create_snapshot(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -1946,7 +2024,8 @@ async def update_snapshot( timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = (), ) -> pubsub.Snapshot: - r"""Updates an existing snapshot. Snapshots are used in + r"""Updates an existing snapshot by updating the fields specified in + the update mask. Snapshots are used in `Seek `__ operations, which allow you to manage message acknowledgments in bulk. That is, you can set the acknowledgment state of messages @@ -2056,6 +2135,9 @@ async def sample_update_snapshot(): ), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -2166,6 +2248,9 @@ async def sample_delete_snapshot(): gapic_v1.routing_header.to_grpc_metadata((("snapshot", request.snapshot),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. await rpc( request, @@ -2261,6 +2346,9 @@ async def sample_seek(): ), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -2380,6 +2468,9 @@ async def set_iam_policy( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -2501,6 +2592,9 @@ async def get_iam_policy( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, @@ -2560,6 +2654,9 @@ async def test_iam_permissions( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._client._validate_universe_domain() + # Send the request. response = await rpc( request, diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index f74e895a3..74fdb1d28 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -31,6 +31,7 @@ Union, cast, ) +import warnings import warnings from google.pubsub_v1 import gapic_version as package_version @@ -46,9 +47,9 @@ from google.oauth2 import service_account # type: ignore try: - OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault] + OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] except AttributeError: # pragma: NO COVER - OptionalRetry = Union[retries.Retry, object] # type: ignore + OptionalRetry = Union[retries.Retry, object, None] # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -137,6 +138,8 @@ def _get_default_mtls_endpoint(api_endpoint): return api_endpoint.replace(".googleapis.com", ".mtls.googleapis.com") + # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead. + # The scopes needed to make gRPC calls to all of the methods defined in # this service _DEFAULT_SCOPES = ( @@ -152,6 +155,9 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + _DEFAULT_ENDPOINT_TEMPLATE = "pubsub.{UNIVERSE_DOMAIN}" + _DEFAULT_UNIVERSE = "googleapis.com" + @classmethod def from_service_account_info(cls, info: dict, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -333,7 +339,7 @@ def parse_common_location_path(path: str) -> Dict[str, str]: def get_mtls_endpoint_and_cert_source( cls, client_options: Optional[client_options_lib.ClientOptions] = None ): - """Return the API endpoint and client cert source for mutual TLS. + """Deprecated. Return the API endpoint and client cert source for mutual TLS. The client cert source is determined in the following order: (1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the @@ -363,6 +369,11 @@ def get_mtls_endpoint_and_cert_source( Raises: google.auth.exceptions.MutualTLSChannelError: If any errors happen. """ + + warnings.warn( + "get_mtls_endpoint_and_cert_source is deprecated. Use the api_endpoint property instead.", + DeprecationWarning, + ) if client_options is None: client_options = client_options_lib.ClientOptions() use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") @@ -396,6 +407,178 @@ def get_mtls_endpoint_and_cert_source( return api_endpoint, client_cert_source + @staticmethod + def _read_environment_variables(): + """Returns the environment variables used by the client. + + Returns: + Tuple[bool, str, str]: returns the GOOGLE_API_USE_CLIENT_CERTIFICATE, + GOOGLE_API_USE_MTLS_ENDPOINT, and GOOGLE_CLOUD_UNIVERSE_DOMAIN environment variables. + + Raises: + ValueError: If GOOGLE_API_USE_CLIENT_CERTIFICATE is not + any of ["true", "false"]. + google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT + is not any of ["auto", "never", "always"]. + """ + use_client_cert = os.getenv( + "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false" + ).lower() + use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower() + universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN") + if use_client_cert not in ("true", "false"): + raise ValueError( + "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) + if use_mtls_endpoint not in ("auto", "never", "always"): + raise MutualTLSChannelError( + "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" + ) + return use_client_cert == "true", use_mtls_endpoint, universe_domain_env + + @staticmethod + def _get_client_cert_source(provided_cert_source, use_cert_flag): + """Return the client cert source to be used by the client. + + Args: + provided_cert_source (bytes): The client certificate source provided. + use_cert_flag (bool): A flag indicating whether to use the client certificate. + + Returns: + bytes or None: The client cert source to be used by the client. + """ + client_cert_source = None + if use_cert_flag: + if provided_cert_source: + client_cert_source = provided_cert_source + elif mtls.has_default_client_cert_source(): + client_cert_source = mtls.default_client_cert_source() + return client_cert_source + + @staticmethod + def _get_api_endpoint( + api_override, client_cert_source, universe_domain, use_mtls_endpoint + ): + """Return the API endpoint used by the client. + + Args: + api_override (str): The API endpoint override. If specified, this is always + the return value of this function and the other arguments are not used. + client_cert_source (bytes): The client certificate source used by the client. + universe_domain (str): The universe domain used by the client. + use_mtls_endpoint (str): How to use the mTLS endpoint, which depends also on the other parameters. + Possible values are "always", "auto", or "never". + + Returns: + str: The API endpoint to be used by the client. + """ + if api_override is not None: + api_endpoint = api_override + elif use_mtls_endpoint == "always" or ( + use_mtls_endpoint == "auto" and client_cert_source + ): + _default_universe = SubscriberClient._DEFAULT_UNIVERSE + if universe_domain != _default_universe: + raise MutualTLSChannelError( + f"mTLS is not supported in any universe other than {_default_universe}." + ) + api_endpoint = SubscriberClient.DEFAULT_MTLS_ENDPOINT + else: + api_endpoint = SubscriberClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=universe_domain + ) + return api_endpoint + + @staticmethod + def _get_universe_domain( + client_universe_domain: Optional[str], universe_domain_env: Optional[str] + ) -> str: + """Return the universe domain used by the client. + + Args: + client_universe_domain (Optional[str]): The universe domain configured via the client options. + universe_domain_env (Optional[str]): The universe domain configured via the "GOOGLE_CLOUD_UNIVERSE_DOMAIN" environment variable. + + Returns: + str: The universe domain to be used by the client. + + Raises: + ValueError: If the universe domain is an empty string. + """ + universe_domain = SubscriberClient._DEFAULT_UNIVERSE + if client_universe_domain is not None: + universe_domain = client_universe_domain + elif universe_domain_env is not None: + universe_domain = universe_domain_env + if len(universe_domain.strip()) == 0: + raise ValueError("Universe Domain cannot be an empty string.") + return universe_domain + + @staticmethod + def _compare_universes( + client_universe: str, credentials: ga_credentials.Credentials + ) -> bool: + """Returns True iff the universe domains used by the client and credentials match. + + Args: + client_universe (str): The universe domain configured via the client options. + credentials (ga_credentials.Credentials): The credentials being used in the client. + + Returns: + bool: True iff client_universe matches the universe in credentials. + + Raises: + ValueError: when client_universe does not match the universe in credentials. + """ + + default_universe = SubscriberClient._DEFAULT_UNIVERSE + credentials_universe = getattr(credentials, "universe_domain", default_universe) + + if client_universe != credentials_universe: + raise ValueError( + "The configured universe domain " + f"({client_universe}) does not match the universe domain " + f"found in the credentials ({credentials_universe}). " + "If you haven't configured the universe domain explicitly, " + f"`{default_universe}` is the default." + ) + return True + + def _validate_universe_domain(self): + """Validates client's and credentials' universe domains are consistent. + + Returns: + bool: True iff the configured universe domain is valid. + + Raises: + ValueError: If the configured universe domain is not valid. + """ + self._is_universe_domain_valid = ( + self._is_universe_domain_valid + or SubscriberClient._compare_universes( + self.universe_domain, self.transport._credentials + ) + ) + return self._is_universe_domain_valid + + @property + def api_endpoint(self): + """Return the API endpoint used by the client instance. + + Returns: + str: The API endpoint used by the client instance. + """ + return self._api_endpoint + + @property + def universe_domain(self) -> str: + """Return the universe domain used by the client instance. + + Returns: + str: The universe domain used by the client instance. + """ + return self._universe_domain + def __init__( self, *, @@ -415,22 +598,32 @@ def __init__( transport (Union[str, SubscriberTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): Custom options for the - client. It won't take effect if a ``transport`` instance is provided. - (1) The ``api_endpoint`` property can be used to override the - default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT - environment variable can also be used to override the endpoint: + client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): + Custom options for the client. + + 1. The ``api_endpoint`` property can be used to override the + default endpoint provided by the client when ``transport`` is + not explicitly provided. Only if this property is not set and + ``transport`` was not explicitly provided, the endpoint is + determined by the GOOGLE_API_USE_MTLS_ENDPOINT environment + variable, which have one of the following values: "always" (always use the default mTLS endpoint), "never" (always - use the default regular endpoint) and "auto" (auto switch to the - default mTLS endpoint if client certificate is present, this is - the default value). However, the ``api_endpoint`` property takes - precedence if provided. - (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + use the default regular endpoint) and "auto" (auto-switch to the + default mTLS endpoint if client certificate is present; this is + the default value). + + 2. If the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable is "true", then the ``client_cert_source`` property can be used - to provide client certificate for mutual TLS transport. If + to provide a client certificate for mTLS transport. If not provided, the default SSL client certificate will be used if present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not set, no client certificate will be used. + + 3. The ``universe_domain`` property can be used to override the + default "googleapis.com" universe. Note that the ``api_endpoint`` + property still takes precedence; and ``universe_domain`` is + currently not supported for mTLS. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): The client info used to send a user-agent string along with API requests. If ``None``, then default info will be used. @@ -441,17 +634,34 @@ def __init__( google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport creation failed for any reason. """ - if isinstance(client_options, dict): - client_options = client_options_lib.from_dict(client_options) - if client_options is None: - client_options = client_options_lib.ClientOptions() - client_options = cast(client_options_lib.ClientOptions, client_options) + self._client_options = client_options + if isinstance(self._client_options, dict): + self._client_options = client_options_lib.from_dict(self._client_options) + if self._client_options is None: + self._client_options = client_options_lib.ClientOptions() + self._client_options = cast( + client_options_lib.ClientOptions, self._client_options + ) - api_endpoint, client_cert_source_func = self.get_mtls_endpoint_and_cert_source( - client_options + universe_domain_opt = getattr(self._client_options, "universe_domain", None) + + ( + self._use_client_cert, + self._use_mtls_endpoint, + self._universe_domain_env, + ) = SubscriberClient._read_environment_variables() + self._client_cert_source = SubscriberClient._get_client_cert_source( + self._client_options.client_cert_source, self._use_client_cert + ) + self._universe_domain = SubscriberClient._get_universe_domain( + universe_domain_opt, self._universe_domain_env ) + self._api_endpoint = None # updated below, depending on `transport` - api_key_value = getattr(client_options, "api_key", None) + # Initialize the universe domain validation. + self._is_universe_domain_valid = False + + api_key_value = getattr(self._client_options, "api_key", None) if api_key_value and credentials: raise ValueError( "client_options.api_key and credentials are mutually exclusive" @@ -460,20 +670,30 @@ def __init__( # Save or instantiate the transport. # Ordinarily, we provide the transport, but allowing a custom transport # instance provides an extensibility point for unusual situations. - if isinstance(transport, SubscriberTransport): + transport_provided = isinstance(transport, SubscriberTransport) + if transport_provided: # transport is a SubscriberTransport instance. - if credentials or client_options.credentials_file or api_key_value: + if credentials or self._client_options.credentials_file or api_key_value: raise ValueError( "When providing a transport instance, " "provide its credentials directly." ) - if client_options.scopes: + if self._client_options.scopes: raise ValueError( "When providing a transport instance, provide its scopes " "directly." ) - self._transport = transport - else: + self._transport = cast(SubscriberTransport, transport) + self._api_endpoint = self._transport.host + + self._api_endpoint = self._api_endpoint or SubscriberClient._get_api_endpoint( + self._client_options.api_endpoint, + self._client_cert_source, + self._universe_domain, + self._use_mtls_endpoint, + ) + + if not transport_provided: import google.auth._default # type: ignore if api_key_value and hasattr( @@ -483,7 +703,7 @@ def __init__( api_key_value ) - Transport = type(self).get_transport_class(transport) + Transport = type(self).get_transport_class(cast(str, transport)) emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") if emulator_host: @@ -495,14 +715,14 @@ def __init__( self._transport = Transport( credentials=credentials, - credentials_file=client_options.credentials_file, - host=api_endpoint, - scopes=client_options.scopes, - client_cert_source_for_mtls=client_cert_source_func, - quota_project_id=client_options.quota_project_id, + credentials_file=self._client_options.credentials_file, + host=self._api_endpoint, + scopes=self._client_options.scopes, + client_cert_source_for_mtls=self._client_cert_source, + quota_project_id=self._client_options.quota_project_id, client_info=client_info, always_use_jwt_access=True, - api_audience=client_options.api_audience, + api_audience=self._client_options.api_audience, ) def create_subscription( @@ -589,21 +809,21 @@ def sample_create_subscription(): on the ``request`` instance; if ``request`` is provided, this should not be set. push_config (google.pubsub_v1.types.PushConfig): - If push delivery is used with this - subscription, this field is used to - configure it. + Optional. If push delivery is used + with this subscription, this field is + used to configure it. This corresponds to the ``push_config`` field on the ``request`` instance; if ``request`` is provided, this should not be set. ack_deadline_seconds (int): - The approximate amount of time (on a best-effort basis) - Pub/Sub waits for the subscriber to acknowledge receipt - before resending the message. In the interval after the - message is delivered and before it is acknowledged, it - is considered to be *outstanding*. During that time - period, the message will not be redelivered (on a - best-effort basis). + Optional. The approximate amount of time (on a + best-effort basis) Pub/Sub waits for the subscriber to + acknowledge receipt before resending the message. In the + interval after the message is delivered and before it is + acknowledged, it is considered to be *outstanding*. + During that time period, the message will not be + redelivered (on a best-effort basis). For pull subscriptions, this value is used as the initial value for the ack deadline. To override this @@ -676,6 +896,9 @@ def sample_create_subscription(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -782,6 +1005,9 @@ def sample_get_subscription(): ), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -803,7 +1029,8 @@ def update_subscription( timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = (), ) -> pubsub.Subscription: - r"""Updates an existing subscription. Note that certain + r"""Updates an existing subscription by updating the + fields specified in the update mask. Note that certain properties of a subscription, such as its topic, are not modifiable. @@ -905,6 +1132,9 @@ def sample_update_subscription(): ), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1009,6 +1239,9 @@ def sample_list_subscriptions(): gapic_v1.routing_header.to_grpc_metadata((("project", request.project),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1118,6 +1351,9 @@ def sample_delete_subscription(): ), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. rpc( request, @@ -1195,8 +1431,8 @@ def sample_modify_ack_deadline(): client. This typically results in an increase in the rate of message redeliveries (that is, duplicates). The minimum deadline you can specify is 0 seconds. The - maximum deadline you can specify is 600 seconds (10 - minutes). + maximum deadline you can specify in a single request is + 600 seconds (10 minutes). This corresponds to the ``ack_deadline_seconds`` field on the ``request`` instance; if ``request`` is provided, this @@ -1244,6 +1480,9 @@ def sample_modify_ack_deadline(): ), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. rpc( request, @@ -1355,6 +1594,9 @@ def sample_acknowledge(): ), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. rpc( request, @@ -1491,6 +1733,9 @@ def sample_pull(): ), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1585,6 +1830,9 @@ def request_generator(): # and friendly error handling. rpc = self._transport._wrapped_methods[self._transport.streaming_pull] + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( requests, @@ -1701,6 +1949,9 @@ def sample_modify_push_config(): ), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. rpc( request, @@ -1808,6 +2059,9 @@ def sample_get_snapshot(): gapic_v1.routing_header.to_grpc_metadata((("snapshot", request.snapshot),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -1916,6 +2170,9 @@ def sample_list_snapshots(): gapic_v1.routing_header.to_grpc_metadata((("project", request.project),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -2073,6 +2330,9 @@ def sample_create_snapshot(): gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -2094,7 +2354,8 @@ def update_snapshot( timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = (), ) -> pubsub.Snapshot: - r"""Updates an existing snapshot. Snapshots are used in + r"""Updates an existing snapshot by updating the fields specified in + the update mask. Snapshots are used in `Seek `__ operations, which allow you to manage message acknowledgments in bulk. That is, you can set the acknowledgment state of messages @@ -2195,6 +2456,9 @@ def sample_update_snapshot(): ), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -2296,6 +2560,9 @@ def sample_delete_snapshot(): gapic_v1.routing_header.to_grpc_metadata((("snapshot", request.snapshot),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. rpc( request, @@ -2381,6 +2648,9 @@ def sample_seek(): ), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -2514,6 +2784,9 @@ def set_iam_policy( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -2635,6 +2908,9 @@ def get_iam_policy( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, @@ -2694,6 +2970,9 @@ def test_iam_permissions( gapic_v1.routing_header.to_grpc_metadata((("resource", request.resource),)), ) + # Validate the universe domain. + self._validate_universe_domain() + # Send the request. response = rpc( request, diff --git a/google/pubsub_v1/services/subscriber/transports/base.py b/google/pubsub_v1/services/subscriber/transports/base.py index d50b8baf6..cea627d76 100644 --- a/google/pubsub_v1/services/subscriber/transports/base.py +++ b/google/pubsub_v1/services/subscriber/transports/base.py @@ -63,7 +63,7 @@ def __init__( Args: host (Optional[str]): - The hostname to connect to. + The hostname to connect to (default: 'pubsub.googleapis.com'). credentials (Optional[google.auth.credentials.Credentials]): The authorization credentials to attach to requests. These credentials identify the application to the service; if none @@ -126,6 +126,10 @@ def __init__( host += ":443" self._host = host + @property + def host(self): + return self._host + def _prep_wrapped_messages(self, client_info): # Precompute the wrapped methods. self._wrapped_methods = { @@ -255,7 +259,7 @@ def _prep_wrapped_messages(self, client_info): default_retry=retries.Retry( initial=0.1, maximum=60.0, - multiplier=4.0, + multiplier=4, predicate=retries.if_exception_type( core_exceptions.Aborted, core_exceptions.DeadlineExceeded, diff --git a/google/pubsub_v1/services/subscriber/transports/grpc.py b/google/pubsub_v1/services/subscriber/transports/grpc.py index 0b1fcd3aa..85a1942af 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc.py @@ -70,7 +70,7 @@ def __init__( Args: host (Optional[str]): - The hostname to connect to. + The hostname to connect to (default: 'pubsub.googleapis.com'). credentials (Optional[google.auth.credentials.Credentials]): The authorization credentials to attach to requests. These credentials identify the application to the service; if none @@ -307,7 +307,8 @@ def update_subscription( ) -> Callable[[pubsub.UpdateSubscriptionRequest], pubsub.Subscription]: r"""Return a callable for the update subscription method over gRPC. - Updates an existing subscription. Note that certain + Updates an existing subscription by updating the + fields specified in the update mask. Note that certain properties of a subscription, such as its topic, are not modifiable. @@ -646,7 +647,8 @@ def update_snapshot( ) -> Callable[[pubsub.UpdateSnapshotRequest], pubsub.Snapshot]: r"""Return a callable for the update snapshot method over gRPC. - Updates an existing snapshot. Snapshots are used in + Updates an existing snapshot by updating the fields specified in + the update mask. Snapshots are used in `Seek `__ operations, which allow you to manage message acknowledgments in bulk. That is, you can set the acknowledgment state of messages diff --git a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py index d32730c1e..b4a73baf4 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py @@ -115,7 +115,7 @@ def __init__( Args: host (Optional[str]): - The hostname to connect to. + The hostname to connect to (default: 'pubsub.googleapis.com'). credentials (Optional[google.auth.credentials.Credentials]): The authorization credentials to attach to requests. These credentials identify the application to the service; if none @@ -310,7 +310,8 @@ def update_subscription( ) -> Callable[[pubsub.UpdateSubscriptionRequest], Awaitable[pubsub.Subscription]]: r"""Return a callable for the update subscription method over gRPC. - Updates an existing subscription. Note that certain + Updates an existing subscription by updating the + fields specified in the update mask. Note that certain properties of a subscription, such as its topic, are not modifiable. @@ -659,7 +660,8 @@ def update_snapshot( ) -> Callable[[pubsub.UpdateSnapshotRequest], Awaitable[pubsub.Snapshot]]: r"""Return a callable for the update snapshot method over gRPC. - Updates an existing snapshot. Snapshots are used in + Updates an existing snapshot by updating the fields specified in + the update mask. Snapshots are used in `Seek `__ operations, which allow you to manage message acknowledgments in bulk. That is, you can set the acknowledgment state of messages diff --git a/google/pubsub_v1/services/subscriber/transports/rest.py b/google/pubsub_v1/services/subscriber/transports/rest.py index c633c4115..26dee31f2 100644 --- a/google/pubsub_v1/services/subscriber/transports/rest.py +++ b/google/pubsub_v1/services/subscriber/transports/rest.py @@ -36,9 +36,9 @@ import warnings try: - OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault] + OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] except AttributeError: # pragma: NO COVER - OptionalRetry = Union[retries.Retry, object] # type: ignore + OptionalRetry = Union[retries.Retry, object, None] # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore @@ -547,7 +547,7 @@ def __init__( Args: host (Optional[str]): - The hostname to connect to. + The hostname to connect to (default: 'pubsub.googleapis.com'). credentials (Optional[google.auth.credentials.Credentials]): The authorization credentials to attach to requests. These credentials identify the application to the service; if none diff --git a/google/pubsub_v1/types/__init__.py b/google/pubsub_v1/types/__init__.py index c82fefd13..80135a019 100644 --- a/google/pubsub_v1/types/__init__.py +++ b/google/pubsub_v1/types/__init__.py @@ -30,6 +30,7 @@ GetSnapshotRequest, GetSubscriptionRequest, GetTopicRequest, + IngestionDataSourceSettings, ListSnapshotsRequest, ListSnapshotsResponse, ListSubscriptionsRequest, @@ -107,6 +108,7 @@ "GetSnapshotRequest", "GetSubscriptionRequest", "GetTopicRequest", + "IngestionDataSourceSettings", "ListSnapshotsRequest", "ListSnapshotsResponse", "ListSubscriptionsRequest", diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index ff7becd48..b04d5d745 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -30,6 +30,7 @@ manifest={ "MessageStoragePolicy", "SchemaSettings", + "IngestionDataSourceSettings", "Topic", "PubsubMessage", "GetTopicRequest", @@ -84,21 +85,32 @@ class MessageStoragePolicy(proto.Message): Attributes: allowed_persistence_regions (MutableSequence[str]): - A list of IDs of Google Cloud regions where - messages that are published to the topic may be - persisted in storage. Messages published by - publishers running in non-allowed Google Cloud - regions (or running outside of Google Cloud - altogether) are routed for storage in one of the - allowed regions. An empty list means that no - regions are allowed, and is not a valid - configuration. + Optional. A list of IDs of Google Cloud + regions where messages that are published to the + topic may be persisted in storage. Messages + published by publishers running in non-allowed + Google Cloud regions (or running outside of + Google Cloud altogether) are routed for storage + in one of the allowed regions. An empty list + means that no regions are allowed, and is not a + valid configuration. + enforce_in_transit (bool): + Optional. If true, ``allowed_persistence_regions`` is also + used to enforce in-transit guarantees for messages. That is, + Pub/Sub will fail Publish operations on this topic and + subscribe operations on any subscription attached to this + topic in any region that is not in + ``allowed_persistence_regions``. """ allowed_persistence_regions: MutableSequence[str] = proto.RepeatedField( proto.STRING, number=1, ) + enforce_in_transit: bool = proto.Field( + proto.BOOL, + number=2, + ) class SchemaSettings(proto.Message): @@ -112,17 +124,18 @@ class SchemaSettings(proto.Message): field will be ``_deleted-schema_`` if the schema has been deleted. encoding (google.pubsub_v1.types.Encoding): - The encoding of messages validated against ``schema``. + Optional. The encoding of messages validated against + ``schema``. first_revision_id (str): - The minimum (inclusive) revision allowed for validating - messages. If empty or not present, allow any revision to be - validated against last_revision or any revision created - before. + Optional. The minimum (inclusive) revision allowed for + validating messages. If empty or not present, allow any + revision to be validated against last_revision or any + revision created before. last_revision_id (str): - The maximum (inclusive) revision allowed for validating - messages. If empty or not present, allow any revision to be - validated against first_revision or any revision created - after. + Optional. The maximum (inclusive) revision allowed for + validating messages. If empty or not present, allow any + revision to be validated against first_revision or any + revision created after. """ schema: str = proto.Field( @@ -144,6 +157,116 @@ class SchemaSettings(proto.Message): ) +class IngestionDataSourceSettings(proto.Message): + r"""Settings for an ingestion data source on a topic. + + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields + + Attributes: + aws_kinesis (google.pubsub_v1.types.IngestionDataSourceSettings.AwsKinesis): + Optional. Amazon Kinesis Data Streams. + + This field is a member of `oneof`_ ``source``. + """ + + class AwsKinesis(proto.Message): + r"""Ingestion settings for Amazon Kinesis Data Streams. + + Attributes: + state (google.pubsub_v1.types.IngestionDataSourceSettings.AwsKinesis.State): + Output only. An output-only field that + indicates the state of the Kinesis ingestion + source. + stream_arn (str): + Required. The Kinesis stream ARN to ingest + data from. + consumer_arn (str): + Required. The Kinesis consumer ARN to used + for ingestion in Enhanced Fan-Out mode. The + consumer must be already created and ready to be + used. + aws_role_arn (str): + Required. AWS role ARN to be used for + Federated Identity authentication with Kinesis. + Check the Pub/Sub docs for how to set up this + role and the required permissions that need to + be attached to it. + gcp_service_account (str): + Required. The GCP service account to be used for Federated + Identity authentication with Kinesis (via a + ``AssumeRoleWithWebIdentity`` call for the provided role). + The ``aws_role_arn`` must be set up with + ``accounts.google.com:sub`` equals to this service account + number. + """ + + class State(proto.Enum): + r"""Possible states for managed ingestion from Amazon Kinesis + Data Streams. + + Values: + STATE_UNSPECIFIED (0): + Default value. This value is unused. + ACTIVE (1): + Ingestion is active. + KINESIS_PERMISSION_DENIED (2): + Permission denied encountered while consuming data from + Kinesis. This can happen if: + + - The provided ``aws_role_arn`` does not exist or does not + have the appropriate permissions attached. + - The provided ``aws_role_arn`` is not set up properly for + Identity Federation using ``gcp_service_account``. + - The Pub/Sub SA is not granted the + ``iam.serviceAccounts.getOpenIdToken`` permission on + ``gcp_service_account``. + PUBLISH_PERMISSION_DENIED (3): + Permission denied encountered while publishing to the topic. + This can happen due to Pub/Sub SA has not been granted the + `appropriate publish + permissions `__ + STREAM_NOT_FOUND (4): + The Kinesis stream does not exist. + CONSUMER_NOT_FOUND (5): + The Kinesis consumer does not exist. + """ + STATE_UNSPECIFIED = 0 + ACTIVE = 1 + KINESIS_PERMISSION_DENIED = 2 + PUBLISH_PERMISSION_DENIED = 3 + STREAM_NOT_FOUND = 4 + CONSUMER_NOT_FOUND = 5 + + state: "IngestionDataSourceSettings.AwsKinesis.State" = proto.Field( + proto.ENUM, + number=1, + enum="IngestionDataSourceSettings.AwsKinesis.State", + ) + stream_arn: str = proto.Field( + proto.STRING, + number=2, + ) + consumer_arn: str = proto.Field( + proto.STRING, + number=3, + ) + aws_role_arn: str = proto.Field( + proto.STRING, + number=4, + ) + gcp_service_account: str = proto.Field( + proto.STRING, + number=5, + ) + + aws_kinesis: AwsKinesis = proto.Field( + proto.MESSAGE, + number=1, + oneof="source", + message=AwsKinesis, + ) + + class Topic(proto.Message): r"""A topic resource. @@ -158,30 +281,30 @@ class Topic(proto.Message): 255 characters in length, and it must not start with ``"goog"``. labels (MutableMapping[str, str]): - See [Creating and managing labels] + Optional. See [Creating and managing labels] (https://cloud.google.com/pubsub/docs/labels). message_storage_policy (google.pubsub_v1.types.MessageStoragePolicy): - Policy constraining the set of Google Cloud - Platform regions where messages published to the - topic may be stored. If not present, then no - constraints are in effect. + Optional. Policy constraining the set of + Google Cloud Platform regions where messages + published to the topic may be stored. If not + present, then no constraints are in effect. kms_key_name (str): - The resource name of the Cloud KMS CryptoKey to be used to - protect access to messages published on this topic. + Optional. The resource name of the Cloud KMS CryptoKey to be + used to protect access to messages published on this topic. The expected format is ``projects/*/locations/*/keyRings/*/cryptoKeys/*``. schema_settings (google.pubsub_v1.types.SchemaSettings): - Settings for validating messages published - against a schema. + Optional. Settings for validating messages + published against a schema. satisfies_pzs (bool): - Reserved for future use. This field is set - only in responses from the server; it is ignored - if it is set in any requests. + Optional. Reserved for future use. This field + is set only in responses from the server; it is + ignored if it is set in any requests. message_retention_duration (google.protobuf.duration_pb2.Duration): - Indicates the minimum duration to retain a message after it - is published to the topic. If this field is set, messages - published to the topic in the last + Optional. Indicates the minimum duration to retain a message + after it is published to the topic. If this field is set, + messages published to the topic in the last ``message_retention_duration`` are always available to subscribers. For instance, it allows any attached subscription to `seek to a @@ -190,8 +313,33 @@ class Topic(proto.Message): this field is not set, message retention is controlled by settings on individual subscriptions. Cannot be more than 31 days or less than 10 minutes. + state (google.pubsub_v1.types.Topic.State): + Output only. An output-only field indicating + the state of the topic. + ingestion_data_source_settings (google.pubsub_v1.types.IngestionDataSourceSettings): + Optional. Settings for managed ingestion from + a data source into this topic. """ + class State(proto.Enum): + r"""The state of the topic. + + Values: + STATE_UNSPECIFIED (0): + Default value. This value is unused. + ACTIVE (1): + The topic does not have any persistent + errors. + INGESTION_RESOURCE_ERROR (2): + Ingestion from the data source has + encountered a permanent error. See the more + detailed error state in the corresponding + ingestion source configuration. + """ + STATE_UNSPECIFIED = 0 + ACTIVE = 1 + INGESTION_RESOURCE_ERROR = 2 + name: str = proto.Field( proto.STRING, number=1, @@ -224,6 +372,16 @@ class Topic(proto.Message): number=8, message=duration_pb2.Duration, ) + state: State = proto.Field( + proto.ENUM, + number=9, + enum=State, + ) + ingestion_data_source_settings: "IngestionDataSourceSettings" = proto.Field( + proto.MESSAGE, + number=10, + message="IngestionDataSourceSettings", + ) class PubsubMessage(proto.Message): @@ -239,14 +397,14 @@ class PubsubMessage(proto.Message): Attributes: data (bytes): - The message data field. If this field is - empty, the message must contain at least one - attribute. + Optional. The message data field. If this + field is empty, the message must contain at + least one attribute. attributes (MutableMapping[str, str]): - Attributes for this message. If this field is - empty, the message must contain non-empty data. - This can be used to filter messages on the - subscription. + Optional. Attributes for this message. If + this field is empty, the message must contain + non-empty data. This can be used to filter + messages on the subscription. message_id (str): ID of this message, assigned by the server when the message is published. Guaranteed to be unique within the topic. This @@ -259,15 +417,15 @@ class PubsubMessage(proto.Message): the server when it receives the ``Publish`` call. It must not be populated by the publisher in a ``Publish`` call. ordering_key (str): - If non-empty, identifies related messages for which publish - order should be respected. If a ``Subscription`` has - ``enable_message_ordering`` set to ``true``, messages - published with the same non-empty ``ordering_key`` value - will be delivered to subscribers in the order in which they - are received by the Pub/Sub system. All ``PubsubMessage``\ s - published in a given ``PublishRequest`` must specify the - same ``ordering_key`` value. For more information, see - `ordering + Optional. If non-empty, identifies related messages for + which publish order should be respected. If a + ``Subscription`` has ``enable_message_ordering`` set to + ``true``, messages published with the same non-empty + ``ordering_key`` value will be delivered to subscribers in + the order in which they are received by the Pub/Sub system. + All ``PubsubMessage``\ s published in a given + ``PublishRequest`` must specify the same ``ordering_key`` + value. For more information, see `ordering messages `__. """ @@ -364,10 +522,10 @@ class PublishResponse(proto.Message): Attributes: message_ids (MutableSequence[str]): - The server-assigned ID of each published - message, in the same order as the messages in - the request. IDs are guaranteed to be unique - within the topic. + Optional. The server-assigned ID of each + published message, in the same order as the + messages in the request. IDs are guaranteed to + be unique within the topic. """ message_ids: MutableSequence[str] = proto.RepeatedField( @@ -384,12 +542,12 @@ class ListTopicsRequest(proto.Message): Required. The name of the project in which to list topics. Format is ``projects/{project-id}``. page_size (int): - Maximum number of topics to return. + Optional. Maximum number of topics to return. page_token (str): - The value returned by the last ``ListTopicsResponse``; - indicates that this is a continuation of a prior - ``ListTopics`` call, and that the system should return the - next page of data. + Optional. The value returned by the last + ``ListTopicsResponse``; indicates that this is a + continuation of a prior ``ListTopics`` call, and that the + system should return the next page of data. """ project: str = proto.Field( @@ -411,11 +569,11 @@ class ListTopicsResponse(proto.Message): Attributes: topics (MutableSequence[google.pubsub_v1.types.Topic]): - The resulting topics. + Optional. The resulting topics. next_page_token (str): - If not empty, indicates that there may be more topics that - match the request; this value should be passed in a new - ``ListTopicsRequest``. + Optional. If not empty, indicates that there may be more + topics that match the request; this value should be passed + in a new ``ListTopicsRequest``. """ @property @@ -442,10 +600,10 @@ class ListTopicSubscriptionsRequest(proto.Message): attached to. Format is ``projects/{project}/topics/{topic}``. page_size (int): - Maximum number of subscription names to - return. + Optional. Maximum number of subscription + names to return. page_token (str): - The value returned by the last + Optional. The value returned by the last ``ListTopicSubscriptionsResponse``; indicates that this is a continuation of a prior ``ListTopicSubscriptions`` call, and that the system should return the next page of data. @@ -470,12 +628,13 @@ class ListTopicSubscriptionsResponse(proto.Message): Attributes: subscriptions (MutableSequence[str]): - The names of subscriptions attached to the - topic specified in the request. + Optional. The names of subscriptions attached + to the topic specified in the request. next_page_token (str): - If not empty, indicates that there may be more subscriptions - that match the request; this value should be passed in a new - ``ListTopicSubscriptionsRequest`` to get more subscriptions. + Optional. If not empty, indicates that there may be more + subscriptions that match the request; this value should be + passed in a new ``ListTopicSubscriptionsRequest`` to get + more subscriptions. """ @property @@ -500,9 +659,10 @@ class ListTopicSnapshotsRequest(proto.Message): Required. The name of the topic that snapshots are attached to. Format is ``projects/{project}/topics/{topic}``. page_size (int): - Maximum number of snapshot names to return. + Optional. Maximum number of snapshot names to + return. page_token (str): - The value returned by the last + Optional. The value returned by the last ``ListTopicSnapshotsResponse``; indicates that this is a continuation of a prior ``ListTopicSnapshots`` call, and that the system should return the next page of data. @@ -527,12 +687,13 @@ class ListTopicSnapshotsResponse(proto.Message): Attributes: snapshots (MutableSequence[str]): - The names of the snapshots that match the - request. + Optional. The names of the snapshots that + match the request. next_page_token (str): - If not empty, indicates that there may be more snapshots - that match the request; this value should be passed in a new - ``ListTopicSnapshotsRequest`` to get more snapshots. + Optional. If not empty, indicates that there may be more + snapshots that match the request; this value should be + passed in a new ``ListTopicSnapshotsRequest`` to get more + snapshots. """ @property @@ -610,24 +771,25 @@ class Subscription(proto.Message): field will be ``_deleted-topic_`` if the topic has been deleted. push_config (google.pubsub_v1.types.PushConfig): - If push delivery is used with this + Optional. If push delivery is used with this subscription, this field is used to configure it. bigquery_config (google.pubsub_v1.types.BigQueryConfig): - If delivery to BigQuery is used with this - subscription, this field is used to configure - it. - cloud_storage_config (google.pubsub_v1.types.CloudStorageConfig): - If delivery to Google Cloud Storage is used + Optional. If delivery to BigQuery is used with this subscription, this field is used to configure it. + cloud_storage_config (google.pubsub_v1.types.CloudStorageConfig): + Optional. If delivery to Google Cloud Storage + is used with this subscription, this field is + used to configure it. ack_deadline_seconds (int): - The approximate amount of time (on a best-effort basis) - Pub/Sub waits for the subscriber to acknowledge receipt - before resending the message. In the interval after the - message is delivered and before it is acknowledged, it is - considered to be *outstanding*. During that time period, the - message will not be redelivered (on a best-effort basis). + Optional. The approximate amount of time (on a best-effort + basis) Pub/Sub waits for the subscriber to acknowledge + receipt before resending the message. In the interval after + the message is delivered and before it is acknowledged, it + is considered to be *outstanding*. During that time period, + the message will not be redelivered (on a best-effort + basis). For pull subscriptions, this value is used as the initial value for the ack deadline. To override this value for a @@ -645,15 +807,16 @@ class Subscription(proto.Message): If the subscriber never acknowledges the message, the Pub/Sub system will eventually redeliver the message. retain_acked_messages (bool): - Indicates whether to retain acknowledged messages. If true, - then messages are not expunged from the subscription's - backlog, even if they are acknowledged, until they fall out - of the ``message_retention_duration`` window. This must be - true if you would like to [``Seek`` to a timestamp] + Optional. Indicates whether to retain acknowledged messages. + If true, then messages are not expunged from the + subscription's backlog, even if they are acknowledged, until + they fall out of the ``message_retention_duration`` window. + This must be true if you would like to [``Seek`` to a + timestamp] (https://cloud.google.com/pubsub/docs/replay-overview#seek_to_a_time) in the past to replay previously-acknowledged messages. message_retention_duration (google.protobuf.duration_pb2.Duration): - How long to retain unacknowledged messages in the + Optional. How long to retain unacknowledged messages in the subscription's backlog, from the moment a message is published. If ``retain_acked_messages`` is true, then this also configures the retention of acknowledged messages, and @@ -661,15 +824,16 @@ class Subscription(proto.Message): Defaults to 7 days. Cannot be more than 7 days or less than 10 minutes. labels (MutableMapping[str, str]): - See `Creating and managing + Optional. See `Creating and managing labels `__. enable_message_ordering (bool): - If true, messages published with the same ``ordering_key`` - in ``PubsubMessage`` will be delivered to the subscribers in - the order in which they are received by the Pub/Sub system. - Otherwise, they may be delivered in any order. + Optional. If true, messages published with the same + ``ordering_key`` in ``PubsubMessage`` will be delivered to + the subscribers in the order in which they are received by + the Pub/Sub system. Otherwise, they may be delivered in any + order. expiration_policy (google.pubsub_v1.types.ExpirationPolicy): - A policy that specifies the conditions for this + Optional. A policy that specifies the conditions for this subscription's expiration. A subscription is considered active as long as any connected subscriber is successfully consuming messages from the subscription or is issuing @@ -680,25 +844,25 @@ class Subscription(proto.Message): is set, but ``expiration_policy.ttl`` is not set, the subscription never expires. filter (str): - An expression written in the Pub/Sub `filter + Optional. An expression written in the Pub/Sub `filter language `__. If non-empty, then only ``PubsubMessage``\ s whose ``attributes`` field matches the filter are delivered on this subscription. If empty, then no messages are filtered out. dead_letter_policy (google.pubsub_v1.types.DeadLetterPolicy): - A policy that specifies the conditions for dead lettering - messages in this subscription. If dead_letter_policy is not - set, dead lettering is disabled. + Optional. A policy that specifies the conditions for dead + lettering messages in this subscription. If + dead_letter_policy is not set, dead lettering is disabled. - The Cloud Pub/Sub service account associated with this + The Pub/Sub service account associated with this subscriptions's parent project (i.e., service-{project_number}@gcp-sa-pubsub.iam.gserviceaccount.com) must have permission to Acknowledge() messages on this subscription. retry_policy (google.pubsub_v1.types.RetryPolicy): - A policy that specifies how Pub/Sub retries - message delivery for this subscription. + Optional. A policy that specifies how Pub/Sub + retries message delivery for this subscription. If not set, the default retry policy is applied. This generally implies that messages will be @@ -707,16 +871,16 @@ class Subscription(proto.Message): NACKs or acknowledgement deadline exceeded events for a given message. detached (bool): - Indicates whether the subscription is detached from its - topic. Detached subscriptions don't receive messages from - their topic and don't retain any backlog. ``Pull`` and - ``StreamingPull`` requests will return FAILED_PRECONDITION. - If the subscription is a push subscription, pushes to the - endpoint will not be made. + Optional. Indicates whether the subscription is detached + from its topic. Detached subscriptions don't receive + messages from their topic and don't retain any backlog. + ``Pull`` and ``StreamingPull`` requests will return + FAILED_PRECONDITION. If the subscription is a push + subscription, pushes to the endpoint will not be made. enable_exactly_once_delivery (bool): - If true, Pub/Sub provides the following guarantees for the - delivery of a message with a given value of ``message_id`` - on this subscription: + Optional. If true, Pub/Sub provides the following guarantees + for the delivery of a message with a given value of + ``message_id`` on this subscription: - The message sent to a subscriber is guaranteed not to be resent before the message's acknowledgement deadline @@ -848,7 +1012,7 @@ class State(proto.Enum): class RetryPolicy(proto.Message): - r"""A policy that specifies how Cloud Pub/Sub retries message delivery. + r"""A policy that specifies how Pub/Sub retries message delivery. Retry delay will be exponential based on provided minimum and maximum backoffs. https://en.wikipedia.org/wiki/Exponential_backoff. @@ -863,15 +1027,15 @@ class RetryPolicy(proto.Message): Attributes: minimum_backoff (google.protobuf.duration_pb2.Duration): - The minimum delay between consecutive - deliveries of a given message. Value should be - between 0 and 600 seconds. Defaults to 10 - seconds. + Optional. The minimum delay between + consecutive deliveries of a given message. Value + should be between 0 and 600 seconds. Defaults to + 10 seconds. maximum_backoff (google.protobuf.duration_pb2.Duration): - The maximum delay between consecutive - deliveries of a given message. Value should be - between 0 and 600 seconds. Defaults to 600 - seconds. + Optional. The maximum delay between + consecutive deliveries of a given message. Value + should be between 0 and 600 seconds. Defaults to + 600 seconds. """ minimum_backoff: duration_pb2.Duration = proto.Field( @@ -896,11 +1060,11 @@ class DeadLetterPolicy(proto.Message): Attributes: dead_letter_topic (str): - The name of the topic to which dead letter messages should - be published. Format is - ``projects/{project}/topics/{topic}``.The Cloud Pub/Sub - service account associated with the enclosing subscription's - parent project (i.e., + Optional. The name of the topic to which dead letter + messages should be published. Format is + ``projects/{project}/topics/{topic}``.The Pub/Sub service + account associated with the enclosing subscription's parent + project (i.e., service-{project_number}@gcp-sa-pubsub.iam.gserviceaccount.com) must have permission to Publish() to this topic. @@ -909,8 +1073,8 @@ class DeadLetterPolicy(proto.Message): topic since messages published to a topic with no subscriptions are lost. max_delivery_attempts (int): - The maximum number of delivery attempts for any message. The - value must be between 5 and 100. + Optional. The maximum number of delivery attempts for any + message. The value must be between 5 and 100. The number of delivery attempts is defined as 1 + (the sum of number of NACKs and number of times the acknowledgement @@ -941,12 +1105,12 @@ class ExpirationPolicy(proto.Message): Attributes: ttl (google.protobuf.duration_pb2.Duration): - Specifies the "time-to-live" duration for an associated - resource. The resource expires if it is not active for a - period of ``ttl``. The definition of "activity" depends on - the type of the associated resource. The minimum and maximum - allowed values for ``ttl`` depend on the type of the - associated resource, as well. If ``ttl`` is not set, the + Optional. Specifies the "time-to-live" duration for an + associated resource. The resource expires if it is not + active for a period of ``ttl``. The definition of "activity" + depends on the type of the associated resource. The minimum + and maximum allowed values for ``ttl`` depend on the type of + the associated resource, as well. If ``ttl`` is not set, the associated resource never expires. """ @@ -969,12 +1133,12 @@ class PushConfig(proto.Message): Attributes: push_endpoint (str): - A URL locating the endpoint to which messages should be - pushed. For example, a Webhook endpoint might use + Optional. A URL locating the endpoint to which messages + should be pushed. For example, a Webhook endpoint might use ``https://example.com/push``. attributes (MutableMapping[str, str]): - Endpoint configuration attributes that can be used to - control different aspects of the message delivery. + Optional. Endpoint configuration attributes that can be used + to control different aspects of the message delivery. The only currently supported attribute is ``x-goog-version``, which you can use to change the format @@ -999,21 +1163,21 @@ class PushConfig(proto.Message): For example: ``attributes { "x-goog-version": "v1" }`` oidc_token (google.pubsub_v1.types.PushConfig.OidcToken): - If specified, Pub/Sub will generate and attach an OIDC JWT - token as an ``Authorization`` header in the HTTP request for - every pushed message. + Optional. If specified, Pub/Sub will generate and attach an + OIDC JWT token as an ``Authorization`` header in the HTTP + request for every pushed message. This field is a member of `oneof`_ ``authentication_method``. pubsub_wrapper (google.pubsub_v1.types.PushConfig.PubsubWrapper): - When set, the payload to the push endpoint is - in the form of the JSON representation of a - PubsubMessage + Optional. When set, the payload to the push + endpoint is in the form of the JSON + representation of a PubsubMessage (https://cloud.google.com/pubsub/docs/reference/rpc/google.pubsub.v1#pubsubmessage). This field is a member of `oneof`_ ``wrapper``. no_wrapper (google.pubsub_v1.types.PushConfig.NoWrapper): - When set, the payload to the push endpoint is - not wrapped. + Optional. When set, the payload to the push + endpoint is not wrapped. This field is a member of `oneof`_ ``wrapper``. """ @@ -1024,19 +1188,20 @@ class OidcToken(proto.Message): Attributes: service_account_email (str): - `Service account + Optional. `Service account email `__ used for generating the OIDC token. For more information on setting up authentication, see `Push subscriptions `__. audience (str): - Audience to be used when generating OIDC - token. The audience claim identifies the + Optional. Audience to be used when generating + OIDC token. The audience claim identifies the recipients that the JWT is intended for. The audience value is a single case-sensitive string. Having multiple values (array) for the audience field is not supported. More info about the OIDC JWT token audience here: + https://tools.ietf.org/html/rfc7519#section-4.1.3 Note: if not specified, the Push endpoint URL will be used. @@ -1063,7 +1228,7 @@ class NoWrapper(proto.Message): Attributes: write_metadata (bool): - When true, writes the Pub/Sub message metadata to + Optional. When true, writes the Pub/Sub message metadata to ``x-goog-pubsub-:`` headers of the HTTP request. Writes the Pub/Sub message attributes to ``:`` headers of the HTTP request. @@ -1108,23 +1273,24 @@ class BigQueryConfig(proto.Message): Attributes: table (str): - The name of the table to which to write data, - of the form {projectId}.{datasetId}.{tableId} + Optional. The name of the table to which to + write data, of the form + {projectId}.{datasetId}.{tableId} use_topic_schema (bool): Optional. When true, use the topic's schema as the columns to write to in BigQuery, if it exists. ``use_topic_schema`` and ``use_table_schema`` cannot be enabled at the same time. write_metadata (bool): - When true, write the subscription name, message_id, - publish_time, attributes, and ordering_key to additional - columns in the table. The subscription name, message_id, and - publish_time fields are put in their own columns while all - other message properties (other than data) are written to a - JSON object in the attributes column. + Optional. When true, write the subscription name, + message_id, publish_time, attributes, and ordering_key to + additional columns in the table. The subscription name, + message_id, and publish_time fields are put in their own + columns while all other message properties (other than data) + are written to a JSON object in the attributes column. drop_unknown_fields (bool): - When true and use_topic_schema is true, any fields that are - a part of the topic schema that are not part of the BigQuery - table schema are dropped when writing to BigQuery. + Optional. When true and use_topic_schema is true, any fields + that are a part of the topic schema that are not part of the + BigQuery table schema are dropped when writing to BigQuery. Otherwise, the schemas must be kept in sync and any messages with extra fields are not written and remain in the subscription's backlog. @@ -1163,12 +1329,17 @@ class State(proto.Enum): SCHEMA_MISMATCH (4): Cannot write to the BigQuery table due to a schema mismatch. + IN_TRANSIT_LOCATION_RESTRICTION (5): + Cannot write to the destination because enforce_in_transit + is set to true and the destination locations are not in the + allowed regions. """ STATE_UNSPECIFIED = 0 ACTIVE = 1 PERMISSION_DENIED = 2 NOT_FOUND = 3 SCHEMA_MISMATCH = 4 + IN_TRANSIT_LOCATION_RESTRICTION = 5 table: str = proto.Field( proto.STRING, @@ -1215,35 +1386,35 @@ class CloudStorageConfig(proto.Message): requirements] (https://cloud.google.com/storage/docs/buckets#naming). filename_prefix (str): - User-provided prefix for Cloud Storage filename. See the - `object naming + Optional. User-provided prefix for Cloud Storage filename. + See the `object naming requirements `__. filename_suffix (str): - User-provided suffix for Cloud Storage filename. See the - `object naming + Optional. User-provided suffix for Cloud Storage filename. + See the `object naming requirements `__. Must not end in "/". text_config (google.pubsub_v1.types.CloudStorageConfig.TextConfig): - If set, message data will be written to Cloud - Storage in text format. + Optional. If set, message data will be + written to Cloud Storage in text format. This field is a member of `oneof`_ ``output_format``. avro_config (google.pubsub_v1.types.CloudStorageConfig.AvroConfig): - If set, message data will be written to Cloud - Storage in Avro format. + Optional. If set, message data will be + written to Cloud Storage in Avro format. This field is a member of `oneof`_ ``output_format``. max_duration (google.protobuf.duration_pb2.Duration): - The maximum duration that can elapse before a - new Cloud Storage file is created. Min 1 minute, - max 10 minutes, default 5 minutes. May not - exceed the subscription's acknowledgement - deadline. + Optional. The maximum duration that can + elapse before a new Cloud Storage file is + created. Min 1 minute, max 10 minutes, default 5 + minutes. May not exceed the subscription's + acknowledgement deadline. max_bytes (int): - The maximum bytes that can be written to a Cloud Storage - file before a new file is created. Min 1 KB, max 10 GiB. The - max_bytes limit may be exceeded in cases where messages are - larger than the limit. + Optional. The maximum bytes that can be written to a Cloud + Storage file before a new file is created. Min 1 KB, max 10 + GiB. The max_bytes limit may be exceeded in cases where + messages are larger than the limit. state (google.pubsub_v1.types.CloudStorageConfig.State): Output only. An output-only field that indicates whether or not the subscription can @@ -1265,11 +1436,16 @@ class State(proto.Enum): NOT_FOUND (3): Cannot write to the Cloud Storage bucket because it does not exist. + IN_TRANSIT_LOCATION_RESTRICTION (4): + Cannot write to the destination because enforce_in_transit + is set to true and the destination locations are not in the + allowed regions. """ STATE_UNSPECIFIED = 0 ACTIVE = 1 PERMISSION_DENIED = 2 NOT_FOUND = 3 + IN_TRANSIT_LOCATION_RESTRICTION = 4 class TextConfig(proto.Message): r"""Configuration for writing message data in text format. @@ -1285,13 +1461,13 @@ class AvroConfig(proto.Message): Attributes: write_metadata (bool): - When true, write the subscription name, message_id, - publish_time, attributes, and ordering_key as additional - fields in the output. The subscription name, message_id, and - publish_time fields are put in their own fields while all - other message properties other than data (for example, an - ordering_key, if present) are added as entries in the - attributes map. + Optional. When true, write the subscription name, + message_id, publish_time, attributes, and ordering_key as + additional fields in the output. The subscription name, + message_id, and publish_time fields are put in their own + fields while all other message properties other than data + (for example, an ordering_key, if present) are added as + entries in the attributes map. """ write_metadata: bool = proto.Field( @@ -1344,12 +1520,12 @@ class ReceivedMessage(proto.Message): Attributes: ack_id (str): - This ID can be used to acknowledge the - received message. + Optional. This ID can be used to acknowledge + the received message. message (google.pubsub_v1.types.PubsubMessage): - The message. + Optional. The message. delivery_attempt (int): - The approximate number of times that Cloud Pub/Sub has + Optional. The approximate number of times that Pub/Sub has attempted to deliver the associated message to a subscriber. More precisely, this is 1 + (number of NACKs) + (number of @@ -1431,9 +1607,10 @@ class ListSubscriptionsRequest(proto.Message): Required. The name of the project in which to list subscriptions. Format is ``projects/{project-id}``. page_size (int): - Maximum number of subscriptions to return. + Optional. Maximum number of subscriptions to + return. page_token (str): - The value returned by the last + Optional. The value returned by the last ``ListSubscriptionsResponse``; indicates that this is a continuation of a prior ``ListSubscriptions`` call, and that the system should return the next page of data. @@ -1458,11 +1635,13 @@ class ListSubscriptionsResponse(proto.Message): Attributes: subscriptions (MutableSequence[google.pubsub_v1.types.Subscription]): - The subscriptions that match the request. + Optional. The subscriptions that match the + request. next_page_token (str): - If not empty, indicates that there may be more subscriptions - that match the request; this value should be passed in a new - ``ListSubscriptionsRequest`` to get more subscriptions. + Optional. If not empty, indicates that there may be more + subscriptions that match the request; this value should be + passed in a new ``ListSubscriptionsRequest`` to get more + subscriptions. """ @property @@ -1566,12 +1745,12 @@ class PullResponse(proto.Message): Attributes: received_messages (MutableSequence[google.pubsub_v1.types.ReceivedMessage]): - Received Pub/Sub messages. The list will be empty if there - are no more messages available in the backlog, or if no - messages could be returned before the request timeout. For - JSON, the response can be entirely empty. The Pub/Sub system - may return fewer than the ``maxMessages`` requested even if - there are more messages available in the backlog. + Optional. Received Pub/Sub messages. The list will be empty + if there are no more messages available in the backlog, or + if no messages could be returned before the request timeout. + For JSON, the response can be entirely empty. The Pub/Sub + system may return fewer than the ``maxMessages`` requested + even if there are more messages available in the backlog. """ received_messages: MutableSequence["ReceivedMessage"] = proto.RepeatedField( @@ -1599,8 +1778,8 @@ class ModifyAckDeadlineRequest(proto.Message): delivery to another subscriber client. This typically results in an increase in the rate of message redeliveries (that is, duplicates). The minimum deadline you can specify - is 0 seconds. The maximum deadline you can specify is 600 - seconds (10 minutes). + is 0 seconds. The maximum deadline you can specify in a + single request is 600 seconds (10 minutes). """ subscription: str = proto.Field( @@ -1655,18 +1834,18 @@ class StreamingPullRequest(proto.Message): client to server. Format is ``projects/{project}/subscriptions/{sub}``. ack_ids (MutableSequence[str]): - List of acknowledgement IDs for acknowledging previously - received messages (received on this stream or a different - stream). If an ack ID has expired, the corresponding message - may be redelivered later. Acknowledging a message more than - once will not result in an error. If the acknowledgement ID - is malformed, the stream will be aborted with status - ``INVALID_ARGUMENT``. + Optional. List of acknowledgement IDs for acknowledging + previously received messages (received on this stream or a + different stream). If an ack ID has expired, the + corresponding message may be redelivered later. + Acknowledging a message more than once will not result in an + error. If the acknowledgement ID is malformed, the stream + will be aborted with status ``INVALID_ARGUMENT``. modify_deadline_seconds (MutableSequence[int]): - The list of new ack deadlines for the IDs listed in - ``modify_deadline_ack_ids``. The size of this list must be - the same as the size of ``modify_deadline_ack_ids``. If it - differs the stream will be aborted with + Optional. The list of new ack deadlines for the IDs listed + in ``modify_deadline_ack_ids``. The size of this list must + be the same as the size of ``modify_deadline_ack_ids``. If + it differs the stream will be aborted with ``INVALID_ARGUMENT``. Each element in this list is applied to the element in the same position in ``modify_deadline_ack_ids``. The new ack deadline is with @@ -1678,8 +1857,8 @@ class StreamingPullRequest(proto.Message): request. If the value is < 0 (an error), the stream will be aborted with status ``INVALID_ARGUMENT``. modify_deadline_ack_ids (MutableSequence[str]): - List of acknowledgement IDs whose deadline will be modified - based on the corresponding element in + Optional. List of acknowledgement IDs whose deadline will be + modified based on the corresponding element in ``modify_deadline_seconds``. This field can be used to indicate that more time is needed to process a message by the subscriber, or to make the message available for @@ -1693,36 +1872,37 @@ class StreamingPullRequest(proto.Message): 10 seconds. The maximum deadline you can specify is 600 seconds (10 minutes). client_id (str): - A unique identifier that is used to distinguish client - instances from each other. Only needs to be provided on the - initial request. When a stream disconnects and reconnects - for the same stream, the client_id should be set to the same - value so that state associated with the old stream can be - transferred to the new stream. The same client_id should not - be used for different client instances. + Optional. A unique identifier that is used to distinguish + client instances from each other. Only needs to be provided + on the initial request. When a stream disconnects and + reconnects for the same stream, the client_id should be set + to the same value so that state associated with the old + stream can be transferred to the new stream. The same + client_id should not be used for different client instances. max_outstanding_messages (int): - Flow control settings for the maximum number of outstanding - messages. When there are ``max_outstanding_messages`` or - more currently sent to the streaming pull client that have - not yet been acked or nacked, the server stops sending more - messages. The sending of messages resumes once the number of - outstanding messages is less than this value. If the value - is <= 0, there is no limit to the number of outstanding - messages. This property can only be set on the initial - StreamingPullRequest. If it is set on a subsequent request, - the stream will be aborted with status ``INVALID_ARGUMENT``. - max_outstanding_bytes (int): - Flow control settings for the maximum number of outstanding - bytes. When there are ``max_outstanding_bytes`` or more - worth of messages currently sent to the streaming pull - client that have not yet been acked or nacked, the server - will stop sending more messages. The sending of messages - resumes once the number of outstanding bytes is less than + Optional. Flow control settings for the maximum number of + outstanding messages. When there are + ``max_outstanding_messages`` currently sent to the streaming + pull client that have not yet been acked or nacked, the + server stops sending more messages. The sending of messages + resumes once the number of outstanding messages is less than this value. If the value is <= 0, there is no limit to the - number of outstanding bytes. This property can only be set - on the initial StreamingPullRequest. If it is set on a + number of outstanding messages. This property can only be + set on the initial StreamingPullRequest. If it is set on a subsequent request, the stream will be aborted with status ``INVALID_ARGUMENT``. + max_outstanding_bytes (int): + Optional. Flow control settings for the maximum number of + outstanding bytes. When there are ``max_outstanding_bytes`` + or more worth of messages currently sent to the streaming + pull client that have not yet been acked or nacked, the + server will stop sending more messages. The sending of + messages resumes once the number of outstanding bytes is + less than this value. If the value is <= 0, there is no + limit to the number of outstanding bytes. This property can + only be set on the initial StreamingPullRequest. If it is + set on a subsequent request, the stream will be aborted with + status ``INVALID_ARGUMENT``. """ subscription: str = proto.Field( @@ -1765,16 +1945,17 @@ class StreamingPullResponse(proto.Message): Attributes: received_messages (MutableSequence[google.pubsub_v1.types.ReceivedMessage]): - Received Pub/Sub messages. This will not be - empty. + Optional. Received Pub/Sub messages. This + will not be empty. acknowledge_confirmation (google.pubsub_v1.types.StreamingPullResponse.AcknowledgeConfirmation): - This field will only be set if + Optional. This field will only be set if ``enable_exactly_once_delivery`` is set to ``true``. modify_ack_deadline_confirmation (google.pubsub_v1.types.StreamingPullResponse.ModifyAckDeadlineConfirmation): - This field will only be set if + Optional. This field will only be set if ``enable_exactly_once_delivery`` is set to ``true``. subscription_properties (google.pubsub_v1.types.StreamingPullResponse.SubscriptionProperties): - Properties associated with this subscription. + Optional. Properties associated with this + subscription. """ class AcknowledgeConfirmation(proto.Message): @@ -1783,17 +1964,18 @@ class AcknowledgeConfirmation(proto.Message): Attributes: ack_ids (MutableSequence[str]): - Successfully processed acknowledgement IDs. + Optional. Successfully processed + acknowledgement IDs. invalid_ack_ids (MutableSequence[str]): - List of acknowledgement IDs that were - malformed or whose acknowledgement deadline has - expired. + Optional. List of acknowledgement IDs that + were malformed or whose acknowledgement deadline + has expired. unordered_ack_ids (MutableSequence[str]): - List of acknowledgement IDs that were out of - order. + Optional. List of acknowledgement IDs that + were out of order. temporary_failed_ack_ids (MutableSequence[str]): - List of acknowledgement IDs that failed - processing with temporary issues. + Optional. List of acknowledgement IDs that + failed processing with temporary issues. """ ack_ids: MutableSequence[str] = proto.RepeatedField( @@ -1819,14 +2001,15 @@ class ModifyAckDeadlineConfirmation(proto.Message): Attributes: ack_ids (MutableSequence[str]): - Successfully processed acknowledgement IDs. + Optional. Successfully processed + acknowledgement IDs. invalid_ack_ids (MutableSequence[str]): - List of acknowledgement IDs that were - malformed or whose acknowledgement deadline has - expired. + Optional. List of acknowledgement IDs that + were malformed or whose acknowledgement deadline + has expired. temporary_failed_ack_ids (MutableSequence[str]): - List of acknowledgement IDs that failed - processing with temporary issues. + Optional. List of acknowledgement IDs that + failed processing with temporary issues. """ ack_ids: MutableSequence[str] = proto.RepeatedField( @@ -1847,11 +2030,11 @@ class SubscriptionProperties(proto.Message): Attributes: exactly_once_delivery_enabled (bool): - True iff exactly once delivery is enabled for - this subscription. + Optional. True iff exactly once delivery is + enabled for this subscription. message_ordering_enabled (bool): - True iff message ordering is enabled for this - subscription. + Optional. True iff message ordering is + enabled for this subscription. """ exactly_once_delivery_enabled: bool = proto.Field( @@ -1909,7 +2092,7 @@ class CreateSnapshotRequest(proto.Message): CreateSnapshot request. Format is ``projects/{project}/subscriptions/{sub}``. labels (MutableMapping[str, str]): - See `Creating and managing + Optional. See `Creating and managing labels `__. """ @@ -1961,16 +2144,16 @@ class Snapshot(proto.Message): Attributes: name (str): - The name of the snapshot. + Optional. The name of the snapshot. topic (str): - The name of the topic from which this - snapshot is retaining messages. + Optional. The name of the topic from which + this snapshot is retaining messages. expire_time (google.protobuf.timestamp_pb2.Timestamp): - The snapshot is guaranteed to exist up until this time. A - newly-created snapshot expires no later than 7 days from the - time of its creation. Its exact lifetime is determined at - creation by the existing backlog in the source subscription. - Specifically, the lifetime of the snapshot is + Optional. The snapshot is guaranteed to exist up until this + time. A newly-created snapshot expires no later than 7 days + from the time of its creation. Its exact lifetime is + determined at creation by the existing backlog in the source + subscription. Specifically, the lifetime of the snapshot is ``7 days - (age of oldest unacked message in the subscription)``. For example, consider a subscription whose oldest unacked message is 3 days old. If a snapshot is created from this @@ -1980,7 +2163,7 @@ class Snapshot(proto.Message): snapshot that would expire in less than 1 hour after creation. labels (MutableMapping[str, str]): - See [Creating and managing labels] + Optional. See [Creating and managing labels] (https://cloud.google.com/pubsub/docs/labels). """ @@ -2027,12 +2210,13 @@ class ListSnapshotsRequest(proto.Message): Required. The name of the project in which to list snapshots. Format is ``projects/{project-id}``. page_size (int): - Maximum number of snapshots to return. + Optional. Maximum number of snapshots to + return. page_token (str): - The value returned by the last ``ListSnapshotsResponse``; - indicates that this is a continuation of a prior - ``ListSnapshots`` call, and that the system should return - the next page of data. + Optional. The value returned by the last + ``ListSnapshotsResponse``; indicates that this is a + continuation of a prior ``ListSnapshots`` call, and that the + system should return the next page of data. """ project: str = proto.Field( @@ -2054,11 +2238,11 @@ class ListSnapshotsResponse(proto.Message): Attributes: snapshots (MutableSequence[google.pubsub_v1.types.Snapshot]): - The resulting snapshots. + Optional. The resulting snapshots. next_page_token (str): - If not empty, indicates that there may be more snapshot that - match the request; this value should be passed in a new - ``ListSnapshotsRequest``. + Optional. If not empty, indicates that there may be more + snapshot that match the request; this value should be passed + in a new ``ListSnapshotsRequest``. """ @property @@ -2105,13 +2289,13 @@ class SeekRequest(proto.Message): subscription (str): Required. The subscription to affect. time (google.protobuf.timestamp_pb2.Timestamp): - The time to seek to. Messages retained in the subscription - that were published before this time are marked as - acknowledged, and messages retained in the subscription that - were published after this time are marked as unacknowledged. - Note that this operation affects only those messages - retained in the subscription (configured by the combination - of ``message_retention_duration`` and + Optional. The time to seek to. Messages retained in the + subscription that were published before this time are marked + as acknowledged, and messages retained in the subscription + that were published after this time are marked as + unacknowledged. Note that this operation affects only those + messages retained in the subscription (configured by the + combination of ``message_retention_duration`` and ``retain_acked_messages``). For example, if ``time`` corresponds to a point before the message retention window (or to a point before the system's notion of the @@ -2121,8 +2305,8 @@ class SeekRequest(proto.Message): This field is a member of `oneof`_ ``target``. snapshot (str): - The snapshot to seek to. The snapshot's topic must be the - same as that of the provided subscription. Format is + Optional. The snapshot to seek to. The snapshot's topic must + be the same as that of the provided subscription. Format is ``projects/{project}/snapshots/{snap}``. This field is a member of `oneof`_ ``target``. diff --git a/google/pubsub_v1/types/schema.py b/google/pubsub_v1/types/schema.py index ff1d22770..11853406d 100644 --- a/google/pubsub_v1/types/schema.py +++ b/google/pubsub_v1/types/schema.py @@ -161,7 +161,7 @@ class CreateSchemaRequest(proto.Message): component of the schema's resource name. See - https://cloud.google.com/pubsub/docs/admin#resource_names + https://cloud.google.com/pubsub/docs/pubsub-basics#resource_names for resource name constraints. """ diff --git a/owlbot.py b/owlbot.py index 0483b04df..c156909ff 100644 --- a/owlbot.py +++ b/owlbot.py @@ -101,7 +101,7 @@ count = s.replace( clients_to_patch, - r"Transport = type\(self\)\.get_transport_class\(transport\)", + r"Transport = type\(self\)\.get_transport_class\(cast\(str, transport\)\)", """\g<0> emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") @@ -324,7 +324,7 @@ if count < 1: raise Exception(".coveragerc replacement failed.") - s.move([library], excludes=["**/gapic_version.py", "README.rst", "docs/**/*", "setup.py", "testing/constraints-3.7.txt"]) + s.move([library], excludes=["**/gapic_version.py", "README.rst", "docs/**/*", "setup.py", "testing/constraints-3.7.txt", "testing/constraints-3.8.txt"]) s.remove_staging_dirs() # ---------------------------------------------------------------------------- diff --git a/scripts/fixup_pubsub_v1_keywords.py b/scripts/fixup_pubsub_v1_keywords.py index 8a294ba36..d8379e9bb 100644 --- a/scripts/fixup_pubsub_v1_keywords.py +++ b/scripts/fixup_pubsub_v1_keywords.py @@ -44,7 +44,7 @@ class pubsubCallTransformer(cst.CSTTransformer): 'create_schema': ('parent', 'schema', 'schema_id', ), 'create_snapshot': ('name', 'subscription', 'labels', ), 'create_subscription': ('name', 'topic', 'push_config', 'bigquery_config', 'cloud_storage_config', 'ack_deadline_seconds', 'retain_acked_messages', 'message_retention_duration', 'labels', 'enable_message_ordering', 'expiration_policy', 'filter', 'dead_letter_policy', 'retry_policy', 'detached', 'enable_exactly_once_delivery', 'topic_message_retention_duration', 'state', ), - 'create_topic': ('name', 'labels', 'message_storage_policy', 'kms_key_name', 'schema_settings', 'satisfies_pzs', 'message_retention_duration', ), + 'create_topic': ('name', 'labels', 'message_storage_policy', 'kms_key_name', 'schema_settings', 'satisfies_pzs', 'message_retention_duration', 'state', 'ingestion_data_source_settings', ), 'delete_schema': ('name', ), 'delete_schema_revision': ('name', 'revision_id', ), 'delete_snapshot': ('snapshot', ), diff --git a/testing/constraints-3.8.txt b/testing/constraints-3.8.txt index ad3f0fa58..30520e2d0 100644 --- a/testing/constraints-3.8.txt +++ b/testing/constraints-3.8.txt @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # This constraints file is required for unit tests. # List all library dependencies and extras in this file. -google-api-core +google-api-core==1.34.0 proto-plus protobuf grpc-google-iam-v1 diff --git a/tests/unit/gapic/pubsub_v1/test_publisher.py b/tests/unit/gapic/pubsub_v1/test_publisher.py index 6511e2f8b..aca4c59dd 100644 --- a/tests/unit/gapic/pubsub_v1/test_publisher.py +++ b/tests/unit/gapic/pubsub_v1/test_publisher.py @@ -29,6 +29,7 @@ import json import math import pytest +from google.api_core import api_core_version from proto.marshal.rules.dates import DurationRule, TimestampRule from proto.marshal.rules import wrappers from requests import Response @@ -75,6 +76,17 @@ def modify_default_endpoint(client): ) +# If default endpoint template is localhost, then default mtls endpoint will be the same. +# This method modifies the default endpoint template so the client can produce a different +# mtls endpoint for endpoint testing purposes. +def modify_default_endpoint_template(client): + return ( + "test.{UNIVERSE_DOMAIN}" + if ("localhost" in client._DEFAULT_ENDPOINT_TEMPLATE) + else client._DEFAULT_ENDPOINT_TEMPLATE + ) + + def test__get_default_mtls_endpoint(): api_endpoint = "example.googleapis.com" api_mtls_endpoint = "example.mtls.googleapis.com" @@ -99,6 +111,254 @@ def test__get_default_mtls_endpoint(): assert PublisherClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi +def test__read_environment_variables(): + assert PublisherClient._read_environment_variables() == (False, "auto", None) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}): + assert PublisherClient._read_environment_variables() == (True, "auto", None) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}): + assert PublisherClient._read_environment_variables() == (False, "auto", None) + + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError) as excinfo: + PublisherClient._read_environment_variables() + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + assert PublisherClient._read_environment_variables() == (False, "never", None) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + assert PublisherClient._read_environment_variables() == (False, "always", None) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}): + assert PublisherClient._read_environment_variables() == (False, "auto", None) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError) as excinfo: + PublisherClient._read_environment_variables() + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" + ) + + with mock.patch.dict(os.environ, {"GOOGLE_CLOUD_UNIVERSE_DOMAIN": "foo.com"}): + assert PublisherClient._read_environment_variables() == ( + False, + "auto", + "foo.com", + ) + + +def test__get_client_cert_source(): + mock_provided_cert_source = mock.Mock() + mock_default_cert_source = mock.Mock() + + assert PublisherClient._get_client_cert_source(None, False) is None + assert ( + PublisherClient._get_client_cert_source(mock_provided_cert_source, False) + is None + ) + assert ( + PublisherClient._get_client_cert_source(mock_provided_cert_source, True) + == mock_provided_cert_source + ) + + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", return_value=True + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=mock_default_cert_source, + ): + assert ( + PublisherClient._get_client_cert_source(None, True) + is mock_default_cert_source + ) + assert ( + PublisherClient._get_client_cert_source( + mock_provided_cert_source, "true" + ) + is mock_provided_cert_source + ) + + +@mock.patch.object( + PublisherClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(PublisherClient), +) +@mock.patch.object( + PublisherAsyncClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(PublisherAsyncClient), +) +def test__get_api_endpoint(): + api_override = "foo.com" + mock_client_cert_source = mock.Mock() + default_universe = PublisherClient._DEFAULT_UNIVERSE + default_endpoint = PublisherClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=default_universe + ) + mock_universe = "bar.com" + mock_endpoint = PublisherClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=mock_universe + ) + + assert ( + PublisherClient._get_api_endpoint( + api_override, mock_client_cert_source, default_universe, "always" + ) + == api_override + ) + assert ( + PublisherClient._get_api_endpoint( + None, mock_client_cert_source, default_universe, "auto" + ) + == PublisherClient.DEFAULT_MTLS_ENDPOINT + ) + assert ( + PublisherClient._get_api_endpoint(None, None, default_universe, "auto") + == default_endpoint + ) + assert ( + PublisherClient._get_api_endpoint(None, None, default_universe, "always") + == PublisherClient.DEFAULT_MTLS_ENDPOINT + ) + assert ( + PublisherClient._get_api_endpoint( + None, mock_client_cert_source, default_universe, "always" + ) + == PublisherClient.DEFAULT_MTLS_ENDPOINT + ) + assert ( + PublisherClient._get_api_endpoint(None, None, mock_universe, "never") + == mock_endpoint + ) + assert ( + PublisherClient._get_api_endpoint(None, None, default_universe, "never") + == default_endpoint + ) + + with pytest.raises(MutualTLSChannelError) as excinfo: + PublisherClient._get_api_endpoint( + None, mock_client_cert_source, mock_universe, "auto" + ) + assert ( + str(excinfo.value) + == "mTLS is not supported in any universe other than googleapis.com." + ) + + +def test__get_universe_domain(): + client_universe_domain = "foo.com" + universe_domain_env = "bar.com" + + assert ( + PublisherClient._get_universe_domain( + client_universe_domain, universe_domain_env + ) + == client_universe_domain + ) + assert ( + PublisherClient._get_universe_domain(None, universe_domain_env) + == universe_domain_env + ) + assert ( + PublisherClient._get_universe_domain(None, None) + == PublisherClient._DEFAULT_UNIVERSE + ) + + with pytest.raises(ValueError) as excinfo: + PublisherClient._get_universe_domain("", None) + assert str(excinfo.value) == "Universe Domain cannot be an empty string." + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (PublisherClient, transports.PublisherGrpcTransport, "grpc"), + (PublisherClient, transports.PublisherRestTransport, "rest"), + ], +) +def test__validate_universe_domain(client_class, transport_class, transport_name): + client = client_class( + transport=transport_class(credentials=ga_credentials.AnonymousCredentials()) + ) + assert client._validate_universe_domain() == True + + # Test the case when universe is already validated. + assert client._validate_universe_domain() == True + + if transport_name == "grpc": + # Test the case where credentials are provided by the + # `local_channel_credentials`. The default universes in both match. + channel = grpc.secure_channel( + "http://localhost/", grpc.local_channel_credentials() + ) + client = client_class(transport=transport_class(channel=channel)) + assert client._validate_universe_domain() == True + + # Test the case where credentials do not exist: e.g. a transport is provided + # with no credentials. Validation should still succeed because there is no + # mismatch with non-existent credentials. + channel = grpc.secure_channel( + "http://localhost/", grpc.local_channel_credentials() + ) + transport = transport_class(channel=channel) + transport._credentials = None + client = client_class(transport=transport) + assert client._validate_universe_domain() == True + + # TODO: This is needed to cater for older versions of google-auth + # Make this test unconditional once the minimum supported version of + # google-auth becomes 2.23.0 or higher. + google_auth_major, google_auth_minor = [ + int(part) for part in google.auth.__version__.split(".")[0:2] + ] + if google_auth_major > 2 or (google_auth_major == 2 and google_auth_minor >= 23): + credentials = ga_credentials.AnonymousCredentials() + credentials._universe_domain = "foo.com" + # Test the case when there is a universe mismatch from the credentials. + client = client_class(transport=transport_class(credentials=credentials)) + with pytest.raises(ValueError) as excinfo: + client._validate_universe_domain() + assert ( + str(excinfo.value) + == "The configured universe domain (googleapis.com) does not match the universe domain found in the credentials (foo.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default." + ) + + # Test the case when there is a universe mismatch from the client. + # + # TODO: Make this test unconditional once the minimum supported version of + # google-api-core becomes 2.15.0 or higher. + api_core_major, api_core_minor = [ + int(part) for part in api_core_version.__version__.split(".")[0:2] + ] + if api_core_major > 2 or (api_core_major == 2 and api_core_minor >= 15): + client = client_class( + client_options={"universe_domain": "bar.com"}, + transport=transport_class( + credentials=ga_credentials.AnonymousCredentials(), + ), + ) + with pytest.raises(ValueError) as excinfo: + client._validate_universe_domain() + assert ( + str(excinfo.value) + == "The configured universe domain (bar.com) does not match the universe domain found in the credentials (googleapis.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default." + ) + + # Test that ValueError is raised if universe_domain is provided via client options and credentials is None + with pytest.raises(ValueError): + client._compare_universes("foo.bar", None) + + @pytest.mark.parametrize( "client_class,transport_name", [ @@ -209,12 +469,14 @@ def test_publisher_client_get_transport_class(): ], ) @mock.patch.object( - PublisherClient, "DEFAULT_ENDPOINT", modify_default_endpoint(PublisherClient) + PublisherClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(PublisherClient), ) @mock.patch.object( PublisherAsyncClient, - "DEFAULT_ENDPOINT", - modify_default_endpoint(PublisherAsyncClient), + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(PublisherAsyncClient), ) def test_publisher_client_client_options(client_class, transport_class, transport_name): # Check that if channel is provided we won't create a new one. @@ -254,7 +516,9 @@ def test_publisher_client_client_options(client_class, transport_class, transpor patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, @@ -284,15 +548,23 @@ def test_publisher_client_client_options(client_class, transport_class, transpor # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has # unsupported value. with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): + with pytest.raises(MutualTLSChannelError) as excinfo: client = client_class(transport=transport_name) + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" + ) # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. with mock.patch.dict( os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} ): - with pytest.raises(ValueError): + with pytest.raises(ValueError) as excinfo: client = client_class(transport=transport_name) + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) # Check the case quota_project_id is provided options = client_options.ClientOptions(quota_project_id="octopus") @@ -302,7 +574,9 @@ def test_publisher_client_client_options(client_class, transport_class, transpor patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id="octopus", @@ -320,7 +594,9 @@ def test_publisher_client_client_options(client_class, transport_class, transpor patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, @@ -352,12 +628,14 @@ def test_publisher_client_client_options(client_class, transport_class, transpor ], ) @mock.patch.object( - PublisherClient, "DEFAULT_ENDPOINT", modify_default_endpoint(PublisherClient) + PublisherClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(PublisherClient), ) @mock.patch.object( PublisherAsyncClient, - "DEFAULT_ENDPOINT", - modify_default_endpoint(PublisherAsyncClient), + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(PublisherAsyncClient), ) @mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) def test_publisher_client_mtls_env_auto( @@ -380,7 +658,9 @@ def test_publisher_client_mtls_env_auto( if use_client_cert_env == "false": expected_client_cert_source = None - expected_host = client.DEFAULT_ENDPOINT + expected_host = client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ) else: expected_client_cert_source = client_cert_source_callback expected_host = client.DEFAULT_MTLS_ENDPOINT @@ -412,7 +692,9 @@ def test_publisher_client_mtls_env_auto( return_value=client_cert_source_callback, ): if use_client_cert_env == "false": - expected_host = client.DEFAULT_ENDPOINT + expected_host = client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ) expected_client_cert_source = None else: expected_host = client.DEFAULT_MTLS_ENDPOINT @@ -446,7 +728,9 @@ def test_publisher_client_mtls_env_auto( patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, @@ -532,6 +816,113 @@ def test_publisher_client_get_mtls_endpoint_and_cert_source(client_class): assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT assert cert_source == mock_client_cert_source + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError) as excinfo: + client_class.get_mtls_endpoint_and_cert_source() + + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" + ) + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError) as excinfo: + client_class.get_mtls_endpoint_and_cert_source() + + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) + + +@pytest.mark.parametrize("client_class", [PublisherClient, PublisherAsyncClient]) +@mock.patch.object( + PublisherClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(PublisherClient), +) +@mock.patch.object( + PublisherAsyncClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(PublisherAsyncClient), +) +def test_publisher_client_client_api_endpoint(client_class): + mock_client_cert_source = client_cert_source_callback + api_override = "foo.com" + default_universe = PublisherClient._DEFAULT_UNIVERSE + default_endpoint = PublisherClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=default_universe + ) + mock_universe = "bar.com" + mock_endpoint = PublisherClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=mock_universe + ) + + # If ClientOptions.api_endpoint is set and GOOGLE_API_USE_CLIENT_CERTIFICATE="true", + # use ClientOptions.api_endpoint as the api endpoint regardless. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}): + with mock.patch( + "google.auth.transport.requests.AuthorizedSession.configure_mtls_channel" + ): + options = client_options.ClientOptions( + client_cert_source=mock_client_cert_source, api_endpoint=api_override + ) + client = client_class( + client_options=options, + credentials=ga_credentials.AnonymousCredentials(), + ) + assert client.api_endpoint == api_override + + # If ClientOptions.api_endpoint is not set and GOOGLE_API_USE_MTLS_ENDPOINT="never", + # use the _DEFAULT_ENDPOINT_TEMPLATE populated with GDU as the api endpoint. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + client = client_class(credentials=ga_credentials.AnonymousCredentials()) + assert client.api_endpoint == default_endpoint + + # If ClientOptions.api_endpoint is not set and GOOGLE_API_USE_MTLS_ENDPOINT="always", + # use the DEFAULT_MTLS_ENDPOINT as the api endpoint. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + client = client_class(credentials=ga_credentials.AnonymousCredentials()) + assert client.api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT + + # If ClientOptions.api_endpoint is not set, GOOGLE_API_USE_MTLS_ENDPOINT="auto" (default), + # GOOGLE_API_USE_CLIENT_CERTIFICATE="false" (default), default cert source doesn't exist, + # and ClientOptions.universe_domain="bar.com", + # use the _DEFAULT_ENDPOINT_TEMPLATE populated with universe domain as the api endpoint. + options = client_options.ClientOptions() + universe_exists = hasattr(options, "universe_domain") + if universe_exists: + options = client_options.ClientOptions(universe_domain=mock_universe) + client = client_class( + client_options=options, credentials=ga_credentials.AnonymousCredentials() + ) + else: + client = client_class( + client_options=options, credentials=ga_credentials.AnonymousCredentials() + ) + assert client.api_endpoint == ( + mock_endpoint if universe_exists else default_endpoint + ) + assert client.universe_domain == ( + mock_universe if universe_exists else default_universe + ) + + # If ClientOptions does not have a universe domain attribute and GOOGLE_API_USE_MTLS_ENDPOINT="never", + # use the _DEFAULT_ENDPOINT_TEMPLATE populated with GDU as the api endpoint. + options = client_options.ClientOptions() + if hasattr(options, "universe_domain"): + delattr(options, "universe_domain") + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + client = client_class( + client_options=options, credentials=ga_credentials.AnonymousCredentials() + ) + assert client.api_endpoint == default_endpoint + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -558,7 +949,9 @@ def test_publisher_client_client_options_scopes( patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=["1", "2"], client_cert_source_for_mtls=None, quota_project_id=None, @@ -593,7 +986,9 @@ def test_publisher_client_client_options_credentials_file( patched.assert_called_once_with( credentials=None, credentials_file="credentials.json", - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, @@ -646,7 +1041,9 @@ def test_publisher_client_create_channel_credentials_file( patched.assert_called_once_with( credentials=None, credentials_file="credentials.json", - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, @@ -713,6 +1110,7 @@ def test_create_topic(request_type, transport: str = "grpc"): name="name_value", kms_key_name="kms_key_name_value", satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, ) response = client.create_topic(request) @@ -726,6 +1124,7 @@ def test_create_topic(request_type, transport: str = "grpc"): assert response.name == "name_value" assert response.kms_key_name == "kms_key_name_value" assert response.satisfies_pzs is True + assert response.state == pubsub.Topic.State.ACTIVE def test_create_topic_empty_call(): @@ -765,6 +1164,7 @@ async def test_create_topic_async( name="name_value", kms_key_name="kms_key_name_value", satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, ) ) response = await client.create_topic(request) @@ -779,6 +1179,7 @@ async def test_create_topic_async( assert response.name == "name_value" assert response.kms_key_name == "kms_key_name_value" assert response.satisfies_pzs is True + assert response.state == pubsub.Topic.State.ACTIVE @pytest.mark.asyncio @@ -949,6 +1350,7 @@ def test_update_topic(request_type, transport: str = "grpc"): name="name_value", kms_key_name="kms_key_name_value", satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, ) response = client.update_topic(request) @@ -962,6 +1364,7 @@ def test_update_topic(request_type, transport: str = "grpc"): assert response.name == "name_value" assert response.kms_key_name == "kms_key_name_value" assert response.satisfies_pzs is True + assert response.state == pubsub.Topic.State.ACTIVE def test_update_topic_empty_call(): @@ -1001,6 +1404,7 @@ async def test_update_topic_async( name="name_value", kms_key_name="kms_key_name_value", satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, ) ) response = await client.update_topic(request) @@ -1015,6 +1419,7 @@ async def test_update_topic_async( assert response.name == "name_value" assert response.kms_key_name == "kms_key_name_value" assert response.satisfies_pzs is True + assert response.state == pubsub.Topic.State.ACTIVE @pytest.mark.asyncio @@ -1333,8 +1738,7 @@ def test_publish_flattened(): # Call the method with a truthy value for each flattened field, # using the keyword arguments to the method. client.publish( - topic="topic_value", - messages=[pubsub.PubsubMessage(data=b"data_blob")], + topic="topic_value", messages=[pubsub.PubsubMessage(data=b"data_blob")] ) # Establish that the underlying call was made with the expected @@ -1437,6 +1841,7 @@ def test_get_topic(request_type, transport: str = "grpc"): name="name_value", kms_key_name="kms_key_name_value", satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, ) response = client.get_topic(request) @@ -1450,6 +1855,7 @@ def test_get_topic(request_type, transport: str = "grpc"): assert response.name == "name_value" assert response.kms_key_name == "kms_key_name_value" assert response.satisfies_pzs is True + assert response.state == pubsub.Topic.State.ACTIVE def test_get_topic_empty_call(): @@ -1489,6 +1895,7 @@ async def test_get_topic_async( name="name_value", kms_key_name="kms_key_name_value", satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, ) ) response = await client.get_topic(request) @@ -1503,6 +1910,7 @@ async def test_get_topic_async( assert response.name == "name_value" assert response.kms_key_name == "kms_key_name_value" assert response.satisfies_pzs is True + assert response.state == pubsub.Topic.State.ACTIVE @pytest.mark.asyncio @@ -1883,7 +2291,7 @@ async def test_list_topics_flattened_error_async(): def test_list_topics_pager(transport_name: str = "grpc"): client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), transport=transport_name, ) @@ -1933,7 +2341,7 @@ def test_list_topics_pager(transport_name: str = "grpc"): def test_list_topics_pages(transport_name: str = "grpc"): client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), transport=transport_name, ) @@ -1975,7 +2383,7 @@ def test_list_topics_pages(transport_name: str = "grpc"): @pytest.mark.asyncio async def test_list_topics_async_pager(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2025,7 +2433,7 @@ async def test_list_topics_async_pager(): @pytest.mark.asyncio async def test_list_topics_async_pages(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2323,7 +2731,7 @@ async def test_list_topic_subscriptions_flattened_error_async(): def test_list_topic_subscriptions_pager(transport_name: str = "grpc"): client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), transport=transport_name, ) @@ -2375,7 +2783,7 @@ def test_list_topic_subscriptions_pager(transport_name: str = "grpc"): def test_list_topic_subscriptions_pages(transport_name: str = "grpc"): client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), transport=transport_name, ) @@ -2419,7 +2827,7 @@ def test_list_topic_subscriptions_pages(transport_name: str = "grpc"): @pytest.mark.asyncio async def test_list_topic_subscriptions_async_pager(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2471,7 +2879,7 @@ async def test_list_topic_subscriptions_async_pager(): @pytest.mark.asyncio async def test_list_topic_subscriptions_async_pages(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2771,7 +3179,7 @@ async def test_list_topic_snapshots_flattened_error_async(): def test_list_topic_snapshots_pager(transport_name: str = "grpc"): client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), transport=transport_name, ) @@ -2823,7 +3231,7 @@ def test_list_topic_snapshots_pager(transport_name: str = "grpc"): def test_list_topic_snapshots_pages(transport_name: str = "grpc"): client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), transport=transport_name, ) @@ -2867,7 +3275,7 @@ def test_list_topic_snapshots_pages(transport_name: str = "grpc"): @pytest.mark.asyncio async def test_list_topic_snapshots_async_pager(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2919,7 +3327,7 @@ async def test_list_topic_snapshots_async_pager(): @pytest.mark.asyncio async def test_list_topic_snapshots_async_pages(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -3365,6 +3773,7 @@ def test_create_topic_rest(request_type): name="name_value", kms_key_name="kms_key_name_value", satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, ) # Wrap the value into a proper Response obj @@ -3383,6 +3792,7 @@ def test_create_topic_rest(request_type): assert response.name == "name_value" assert response.kms_key_name == "kms_key_name_value" assert response.satisfies_pzs is True + assert response.state == pubsub.Topic.State.ACTIVE def test_create_topic_rest_required_fields(request_type=pubsub.Topic): @@ -3631,6 +4041,7 @@ def test_update_topic_rest(request_type): name="name_value", kms_key_name="kms_key_name_value", satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, ) # Wrap the value into a proper Response obj @@ -3649,6 +4060,7 @@ def test_update_topic_rest(request_type): assert response.name == "name_value" assert response.kms_key_name == "kms_key_name_value" assert response.satisfies_pzs is True + assert response.state == pubsub.Topic.State.ACTIVE def test_update_topic_rest_required_fields(request_type=pubsub.UpdateTopicRequest): @@ -4177,6 +4589,7 @@ def test_get_topic_rest(request_type): name="name_value", kms_key_name="kms_key_name_value", satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, ) # Wrap the value into a proper Response obj @@ -4195,6 +4608,7 @@ def test_get_topic_rest(request_type): assert response.name == "name_value" assert response.kms_key_name == "kms_key_name_value" assert response.satisfies_pzs is True + assert response.state == pubsub.Topic.State.ACTIVE def test_get_topic_rest_required_fields(request_type=pubsub.GetTopicRequest): @@ -5921,7 +6335,7 @@ def test_credentials_transport_error(): ) # It is an error to provide an api_key and a credential. - options = mock.Mock() + options = client_options.ClientOptions() options.api_key = "api_key" with pytest.raises(ValueError): client = PublisherClient( @@ -7407,7 +7821,9 @@ def test_api_key_credentials(client_class, transport_class): patched.assert_called_once_with( credentials=mock_cred, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, diff --git a/tests/unit/gapic/pubsub_v1/test_schema_service.py b/tests/unit/gapic/pubsub_v1/test_schema_service.py index c3585ae3b..e73ef9114 100644 --- a/tests/unit/gapic/pubsub_v1/test_schema_service.py +++ b/tests/unit/gapic/pubsub_v1/test_schema_service.py @@ -29,6 +29,7 @@ import json import math import pytest +from google.api_core import api_core_version from proto.marshal.rules.dates import DurationRule, TimestampRule from proto.marshal.rules import wrappers from requests import Response @@ -73,6 +74,17 @@ def modify_default_endpoint(client): ) +# If default endpoint template is localhost, then default mtls endpoint will be the same. +# This method modifies the default endpoint template so the client can produce a different +# mtls endpoint for endpoint testing purposes. +def modify_default_endpoint_template(client): + return ( + "test.{UNIVERSE_DOMAIN}" + if ("localhost" in client._DEFAULT_ENDPOINT_TEMPLATE) + else client._DEFAULT_ENDPOINT_TEMPLATE + ) + + def test__get_default_mtls_endpoint(): api_endpoint = "example.googleapis.com" api_mtls_endpoint = "example.mtls.googleapis.com" @@ -102,6 +114,270 @@ def test__get_default_mtls_endpoint(): ) +def test__read_environment_variables(): + assert SchemaServiceClient._read_environment_variables() == (False, "auto", None) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}): + assert SchemaServiceClient._read_environment_variables() == (True, "auto", None) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}): + assert SchemaServiceClient._read_environment_variables() == ( + False, + "auto", + None, + ) + + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError) as excinfo: + SchemaServiceClient._read_environment_variables() + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + assert SchemaServiceClient._read_environment_variables() == ( + False, + "never", + None, + ) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + assert SchemaServiceClient._read_environment_variables() == ( + False, + "always", + None, + ) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}): + assert SchemaServiceClient._read_environment_variables() == ( + False, + "auto", + None, + ) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError) as excinfo: + SchemaServiceClient._read_environment_variables() + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" + ) + + with mock.patch.dict(os.environ, {"GOOGLE_CLOUD_UNIVERSE_DOMAIN": "foo.com"}): + assert SchemaServiceClient._read_environment_variables() == ( + False, + "auto", + "foo.com", + ) + + +def test__get_client_cert_source(): + mock_provided_cert_source = mock.Mock() + mock_default_cert_source = mock.Mock() + + assert SchemaServiceClient._get_client_cert_source(None, False) is None + assert ( + SchemaServiceClient._get_client_cert_source(mock_provided_cert_source, False) + is None + ) + assert ( + SchemaServiceClient._get_client_cert_source(mock_provided_cert_source, True) + == mock_provided_cert_source + ) + + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", return_value=True + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=mock_default_cert_source, + ): + assert ( + SchemaServiceClient._get_client_cert_source(None, True) + is mock_default_cert_source + ) + assert ( + SchemaServiceClient._get_client_cert_source( + mock_provided_cert_source, "true" + ) + is mock_provided_cert_source + ) + + +@mock.patch.object( + SchemaServiceClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SchemaServiceClient), +) +@mock.patch.object( + SchemaServiceAsyncClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SchemaServiceAsyncClient), +) +def test__get_api_endpoint(): + api_override = "foo.com" + mock_client_cert_source = mock.Mock() + default_universe = SchemaServiceClient._DEFAULT_UNIVERSE + default_endpoint = SchemaServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=default_universe + ) + mock_universe = "bar.com" + mock_endpoint = SchemaServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=mock_universe + ) + + assert ( + SchemaServiceClient._get_api_endpoint( + api_override, mock_client_cert_source, default_universe, "always" + ) + == api_override + ) + assert ( + SchemaServiceClient._get_api_endpoint( + None, mock_client_cert_source, default_universe, "auto" + ) + == SchemaServiceClient.DEFAULT_MTLS_ENDPOINT + ) + assert ( + SchemaServiceClient._get_api_endpoint(None, None, default_universe, "auto") + == default_endpoint + ) + assert ( + SchemaServiceClient._get_api_endpoint(None, None, default_universe, "always") + == SchemaServiceClient.DEFAULT_MTLS_ENDPOINT + ) + assert ( + SchemaServiceClient._get_api_endpoint( + None, mock_client_cert_source, default_universe, "always" + ) + == SchemaServiceClient.DEFAULT_MTLS_ENDPOINT + ) + assert ( + SchemaServiceClient._get_api_endpoint(None, None, mock_universe, "never") + == mock_endpoint + ) + assert ( + SchemaServiceClient._get_api_endpoint(None, None, default_universe, "never") + == default_endpoint + ) + + with pytest.raises(MutualTLSChannelError) as excinfo: + SchemaServiceClient._get_api_endpoint( + None, mock_client_cert_source, mock_universe, "auto" + ) + assert ( + str(excinfo.value) + == "mTLS is not supported in any universe other than googleapis.com." + ) + + +def test__get_universe_domain(): + client_universe_domain = "foo.com" + universe_domain_env = "bar.com" + + assert ( + SchemaServiceClient._get_universe_domain( + client_universe_domain, universe_domain_env + ) + == client_universe_domain + ) + assert ( + SchemaServiceClient._get_universe_domain(None, universe_domain_env) + == universe_domain_env + ) + assert ( + SchemaServiceClient._get_universe_domain(None, None) + == SchemaServiceClient._DEFAULT_UNIVERSE + ) + + with pytest.raises(ValueError) as excinfo: + SchemaServiceClient._get_universe_domain("", None) + assert str(excinfo.value) == "Universe Domain cannot be an empty string." + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (SchemaServiceClient, transports.SchemaServiceGrpcTransport, "grpc"), + (SchemaServiceClient, transports.SchemaServiceRestTransport, "rest"), + ], +) +def test__validate_universe_domain(client_class, transport_class, transport_name): + client = client_class( + transport=transport_class(credentials=ga_credentials.AnonymousCredentials()) + ) + assert client._validate_universe_domain() == True + + # Test the case when universe is already validated. + assert client._validate_universe_domain() == True + + if transport_name == "grpc": + # Test the case where credentials are provided by the + # `local_channel_credentials`. The default universes in both match. + channel = grpc.secure_channel( + "http://localhost/", grpc.local_channel_credentials() + ) + client = client_class(transport=transport_class(channel=channel)) + assert client._validate_universe_domain() == True + + # Test the case where credentials do not exist: e.g. a transport is provided + # with no credentials. Validation should still succeed because there is no + # mismatch with non-existent credentials. + channel = grpc.secure_channel( + "http://localhost/", grpc.local_channel_credentials() + ) + transport = transport_class(channel=channel) + transport._credentials = None + client = client_class(transport=transport) + assert client._validate_universe_domain() == True + + # TODO: This is needed to cater for older versions of google-auth + # Make this test unconditional once the minimum supported version of + # google-auth becomes 2.23.0 or higher. + google_auth_major, google_auth_minor = [ + int(part) for part in google.auth.__version__.split(".")[0:2] + ] + if google_auth_major > 2 or (google_auth_major == 2 and google_auth_minor >= 23): + credentials = ga_credentials.AnonymousCredentials() + credentials._universe_domain = "foo.com" + # Test the case when there is a universe mismatch from the credentials. + client = client_class(transport=transport_class(credentials=credentials)) + with pytest.raises(ValueError) as excinfo: + client._validate_universe_domain() + assert ( + str(excinfo.value) + == "The configured universe domain (googleapis.com) does not match the universe domain found in the credentials (foo.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default." + ) + + # Test the case when there is a universe mismatch from the client. + # + # TODO: Make this test unconditional once the minimum supported version of + # google-api-core becomes 2.15.0 or higher. + api_core_major, api_core_minor = [ + int(part) for part in api_core_version.__version__.split(".")[0:2] + ] + if api_core_major > 2 or (api_core_major == 2 and api_core_minor >= 15): + client = client_class( + client_options={"universe_domain": "bar.com"}, + transport=transport_class( + credentials=ga_credentials.AnonymousCredentials(), + ), + ) + with pytest.raises(ValueError) as excinfo: + client._validate_universe_domain() + assert ( + str(excinfo.value) + == "The configured universe domain (bar.com) does not match the universe domain found in the credentials (googleapis.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default." + ) + + # Test that ValueError is raised if universe_domain is provided via client options and credentials is None + with pytest.raises(ValueError): + client._compare_universes("foo.bar", None) + + @pytest.mark.parametrize( "client_class,transport_name", [ @@ -213,13 +489,13 @@ def test_schema_service_client_get_transport_class(): ) @mock.patch.object( SchemaServiceClient, - "DEFAULT_ENDPOINT", - modify_default_endpoint(SchemaServiceClient), + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SchemaServiceClient), ) @mock.patch.object( SchemaServiceAsyncClient, - "DEFAULT_ENDPOINT", - modify_default_endpoint(SchemaServiceAsyncClient), + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SchemaServiceAsyncClient), ) def test_schema_service_client_client_options( client_class, transport_class, transport_name @@ -261,7 +537,9 @@ def test_schema_service_client_client_options( patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, @@ -291,15 +569,23 @@ def test_schema_service_client_client_options( # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has # unsupported value. with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): + with pytest.raises(MutualTLSChannelError) as excinfo: client = client_class(transport=transport_name) + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" + ) # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. with mock.patch.dict( os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} ): - with pytest.raises(ValueError): + with pytest.raises(ValueError) as excinfo: client = client_class(transport=transport_name) + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) # Check the case quota_project_id is provided options = client_options.ClientOptions(quota_project_id="octopus") @@ -309,7 +595,9 @@ def test_schema_service_client_client_options( patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id="octopus", @@ -327,7 +615,9 @@ def test_schema_service_client_client_options( patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, @@ -360,13 +650,13 @@ def test_schema_service_client_client_options( ) @mock.patch.object( SchemaServiceClient, - "DEFAULT_ENDPOINT", - modify_default_endpoint(SchemaServiceClient), + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SchemaServiceClient), ) @mock.patch.object( SchemaServiceAsyncClient, - "DEFAULT_ENDPOINT", - modify_default_endpoint(SchemaServiceAsyncClient), + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SchemaServiceAsyncClient), ) @mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) def test_schema_service_client_mtls_env_auto( @@ -389,7 +679,9 @@ def test_schema_service_client_mtls_env_auto( if use_client_cert_env == "false": expected_client_cert_source = None - expected_host = client.DEFAULT_ENDPOINT + expected_host = client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ) else: expected_client_cert_source = client_cert_source_callback expected_host = client.DEFAULT_MTLS_ENDPOINT @@ -421,7 +713,9 @@ def test_schema_service_client_mtls_env_auto( return_value=client_cert_source_callback, ): if use_client_cert_env == "false": - expected_host = client.DEFAULT_ENDPOINT + expected_host = client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ) expected_client_cert_source = None else: expected_host = client.DEFAULT_MTLS_ENDPOINT @@ -455,7 +749,9 @@ def test_schema_service_client_mtls_env_auto( patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, @@ -545,6 +841,115 @@ def test_schema_service_client_get_mtls_endpoint_and_cert_source(client_class): assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT assert cert_source == mock_client_cert_source + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError) as excinfo: + client_class.get_mtls_endpoint_and_cert_source() + + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" + ) + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError) as excinfo: + client_class.get_mtls_endpoint_and_cert_source() + + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) + + +@pytest.mark.parametrize( + "client_class", [SchemaServiceClient, SchemaServiceAsyncClient] +) +@mock.patch.object( + SchemaServiceClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SchemaServiceClient), +) +@mock.patch.object( + SchemaServiceAsyncClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SchemaServiceAsyncClient), +) +def test_schema_service_client_client_api_endpoint(client_class): + mock_client_cert_source = client_cert_source_callback + api_override = "foo.com" + default_universe = SchemaServiceClient._DEFAULT_UNIVERSE + default_endpoint = SchemaServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=default_universe + ) + mock_universe = "bar.com" + mock_endpoint = SchemaServiceClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=mock_universe + ) + + # If ClientOptions.api_endpoint is set and GOOGLE_API_USE_CLIENT_CERTIFICATE="true", + # use ClientOptions.api_endpoint as the api endpoint regardless. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}): + with mock.patch( + "google.auth.transport.requests.AuthorizedSession.configure_mtls_channel" + ): + options = client_options.ClientOptions( + client_cert_source=mock_client_cert_source, api_endpoint=api_override + ) + client = client_class( + client_options=options, + credentials=ga_credentials.AnonymousCredentials(), + ) + assert client.api_endpoint == api_override + + # If ClientOptions.api_endpoint is not set and GOOGLE_API_USE_MTLS_ENDPOINT="never", + # use the _DEFAULT_ENDPOINT_TEMPLATE populated with GDU as the api endpoint. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + client = client_class(credentials=ga_credentials.AnonymousCredentials()) + assert client.api_endpoint == default_endpoint + + # If ClientOptions.api_endpoint is not set and GOOGLE_API_USE_MTLS_ENDPOINT="always", + # use the DEFAULT_MTLS_ENDPOINT as the api endpoint. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + client = client_class(credentials=ga_credentials.AnonymousCredentials()) + assert client.api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT + + # If ClientOptions.api_endpoint is not set, GOOGLE_API_USE_MTLS_ENDPOINT="auto" (default), + # GOOGLE_API_USE_CLIENT_CERTIFICATE="false" (default), default cert source doesn't exist, + # and ClientOptions.universe_domain="bar.com", + # use the _DEFAULT_ENDPOINT_TEMPLATE populated with universe domain as the api endpoint. + options = client_options.ClientOptions() + universe_exists = hasattr(options, "universe_domain") + if universe_exists: + options = client_options.ClientOptions(universe_domain=mock_universe) + client = client_class( + client_options=options, credentials=ga_credentials.AnonymousCredentials() + ) + else: + client = client_class( + client_options=options, credentials=ga_credentials.AnonymousCredentials() + ) + assert client.api_endpoint == ( + mock_endpoint if universe_exists else default_endpoint + ) + assert client.universe_domain == ( + mock_universe if universe_exists else default_universe + ) + + # If ClientOptions does not have a universe domain attribute and GOOGLE_API_USE_MTLS_ENDPOINT="never", + # use the _DEFAULT_ENDPOINT_TEMPLATE populated with GDU as the api endpoint. + options = client_options.ClientOptions() + if hasattr(options, "universe_domain"): + delattr(options, "universe_domain") + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + client = client_class( + client_options=options, credentials=ga_credentials.AnonymousCredentials() + ) + assert client.api_endpoint == default_endpoint + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -571,7 +976,9 @@ def test_schema_service_client_client_options_scopes( patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=["1", "2"], client_cert_source_for_mtls=None, quota_project_id=None, @@ -611,7 +1018,9 @@ def test_schema_service_client_client_options_credentials_file( patched.assert_called_once_with( credentials=None, credentials_file="credentials.json", - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, @@ -671,7 +1080,9 @@ def test_schema_service_client_create_channel_credentials_file( patched.assert_called_once_with( credentials=None, credentials_file="credentials.json", - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, @@ -1448,7 +1859,7 @@ async def test_list_schemas_flattened_error_async(): def test_list_schemas_pager(transport_name: str = "grpc"): client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), transport=transport_name, ) @@ -1498,7 +1909,7 @@ def test_list_schemas_pager(transport_name: str = "grpc"): def test_list_schemas_pages(transport_name: str = "grpc"): client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), transport=transport_name, ) @@ -1540,7 +1951,7 @@ def test_list_schemas_pages(transport_name: str = "grpc"): @pytest.mark.asyncio async def test_list_schemas_async_pager(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -1590,7 +2001,7 @@ async def test_list_schemas_async_pager(): @pytest.mark.asyncio async def test_list_schemas_async_pages(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -1884,7 +2295,7 @@ async def test_list_schema_revisions_flattened_error_async(): def test_list_schema_revisions_pager(transport_name: str = "grpc"): client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), transport=transport_name, ) @@ -1936,7 +2347,7 @@ def test_list_schema_revisions_pager(transport_name: str = "grpc"): def test_list_schema_revisions_pages(transport_name: str = "grpc"): client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), transport=transport_name, ) @@ -1980,7 +2391,7 @@ def test_list_schema_revisions_pages(transport_name: str = "grpc"): @pytest.mark.asyncio async def test_list_schema_revisions_async_pager(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2032,7 +2443,7 @@ async def test_list_schema_revisions_async_pager(): @pytest.mark.asyncio async def test_list_schema_revisions_async_pages(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -6368,7 +6779,7 @@ def test_credentials_transport_error(): ) # It is an error to provide an api_key and a credential. - options = mock.Mock() + options = client_options.ClientOptions() options.api_key = "api_key" with pytest.raises(ValueError): client = SchemaServiceClient( @@ -7821,7 +8232,9 @@ def test_api_key_credentials(client_class, transport_class): patched.assert_called_once_with( credentials=mock_cred, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py index 94f7e301e..4ff6a3f27 100644 --- a/tests/unit/gapic/pubsub_v1/test_subscriber.py +++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py @@ -30,6 +30,7 @@ import json import math import pytest +from google.api_core import api_core_version from proto.marshal.rules.dates import DurationRule, TimestampRule from proto.marshal.rules import wrappers from requests import Response @@ -75,6 +76,17 @@ def modify_default_endpoint(client): ) +# If default endpoint template is localhost, then default mtls endpoint will be the same. +# This method modifies the default endpoint template so the client can produce a different +# mtls endpoint for endpoint testing purposes. +def modify_default_endpoint_template(client): + return ( + "test.{UNIVERSE_DOMAIN}" + if ("localhost" in client._DEFAULT_ENDPOINT_TEMPLATE) + else client._DEFAULT_ENDPOINT_TEMPLATE + ) + + def test__get_default_mtls_endpoint(): api_endpoint = "example.googleapis.com" api_mtls_endpoint = "example.mtls.googleapis.com" @@ -101,6 +113,254 @@ def test__get_default_mtls_endpoint(): assert SubscriberClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi +def test__read_environment_variables(): + assert SubscriberClient._read_environment_variables() == (False, "auto", None) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}): + assert SubscriberClient._read_environment_variables() == (True, "auto", None) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"}): + assert SubscriberClient._read_environment_variables() == (False, "auto", None) + + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError) as excinfo: + SubscriberClient._read_environment_variables() + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + assert SubscriberClient._read_environment_variables() == (False, "never", None) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + assert SubscriberClient._read_environment_variables() == (False, "always", None) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}): + assert SubscriberClient._read_environment_variables() == (False, "auto", None) + + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError) as excinfo: + SubscriberClient._read_environment_variables() + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" + ) + + with mock.patch.dict(os.environ, {"GOOGLE_CLOUD_UNIVERSE_DOMAIN": "foo.com"}): + assert SubscriberClient._read_environment_variables() == ( + False, + "auto", + "foo.com", + ) + + +def test__get_client_cert_source(): + mock_provided_cert_source = mock.Mock() + mock_default_cert_source = mock.Mock() + + assert SubscriberClient._get_client_cert_source(None, False) is None + assert ( + SubscriberClient._get_client_cert_source(mock_provided_cert_source, False) + is None + ) + assert ( + SubscriberClient._get_client_cert_source(mock_provided_cert_source, True) + == mock_provided_cert_source + ) + + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", return_value=True + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=mock_default_cert_source, + ): + assert ( + SubscriberClient._get_client_cert_source(None, True) + is mock_default_cert_source + ) + assert ( + SubscriberClient._get_client_cert_source( + mock_provided_cert_source, "true" + ) + is mock_provided_cert_source + ) + + +@mock.patch.object( + SubscriberClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SubscriberClient), +) +@mock.patch.object( + SubscriberAsyncClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SubscriberAsyncClient), +) +def test__get_api_endpoint(): + api_override = "foo.com" + mock_client_cert_source = mock.Mock() + default_universe = SubscriberClient._DEFAULT_UNIVERSE + default_endpoint = SubscriberClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=default_universe + ) + mock_universe = "bar.com" + mock_endpoint = SubscriberClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=mock_universe + ) + + assert ( + SubscriberClient._get_api_endpoint( + api_override, mock_client_cert_source, default_universe, "always" + ) + == api_override + ) + assert ( + SubscriberClient._get_api_endpoint( + None, mock_client_cert_source, default_universe, "auto" + ) + == SubscriberClient.DEFAULT_MTLS_ENDPOINT + ) + assert ( + SubscriberClient._get_api_endpoint(None, None, default_universe, "auto") + == default_endpoint + ) + assert ( + SubscriberClient._get_api_endpoint(None, None, default_universe, "always") + == SubscriberClient.DEFAULT_MTLS_ENDPOINT + ) + assert ( + SubscriberClient._get_api_endpoint( + None, mock_client_cert_source, default_universe, "always" + ) + == SubscriberClient.DEFAULT_MTLS_ENDPOINT + ) + assert ( + SubscriberClient._get_api_endpoint(None, None, mock_universe, "never") + == mock_endpoint + ) + assert ( + SubscriberClient._get_api_endpoint(None, None, default_universe, "never") + == default_endpoint + ) + + with pytest.raises(MutualTLSChannelError) as excinfo: + SubscriberClient._get_api_endpoint( + None, mock_client_cert_source, mock_universe, "auto" + ) + assert ( + str(excinfo.value) + == "mTLS is not supported in any universe other than googleapis.com." + ) + + +def test__get_universe_domain(): + client_universe_domain = "foo.com" + universe_domain_env = "bar.com" + + assert ( + SubscriberClient._get_universe_domain( + client_universe_domain, universe_domain_env + ) + == client_universe_domain + ) + assert ( + SubscriberClient._get_universe_domain(None, universe_domain_env) + == universe_domain_env + ) + assert ( + SubscriberClient._get_universe_domain(None, None) + == SubscriberClient._DEFAULT_UNIVERSE + ) + + with pytest.raises(ValueError) as excinfo: + SubscriberClient._get_universe_domain("", None) + assert str(excinfo.value) == "Universe Domain cannot be an empty string." + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (SubscriberClient, transports.SubscriberGrpcTransport, "grpc"), + (SubscriberClient, transports.SubscriberRestTransport, "rest"), + ], +) +def test__validate_universe_domain(client_class, transport_class, transport_name): + client = client_class( + transport=transport_class(credentials=ga_credentials.AnonymousCredentials()) + ) + assert client._validate_universe_domain() == True + + # Test the case when universe is already validated. + assert client._validate_universe_domain() == True + + if transport_name == "grpc": + # Test the case where credentials are provided by the + # `local_channel_credentials`. The default universes in both match. + channel = grpc.secure_channel( + "http://localhost/", grpc.local_channel_credentials() + ) + client = client_class(transport=transport_class(channel=channel)) + assert client._validate_universe_domain() == True + + # Test the case where credentials do not exist: e.g. a transport is provided + # with no credentials. Validation should still succeed because there is no + # mismatch with non-existent credentials. + channel = grpc.secure_channel( + "http://localhost/", grpc.local_channel_credentials() + ) + transport = transport_class(channel=channel) + transport._credentials = None + client = client_class(transport=transport) + assert client._validate_universe_domain() == True + + # TODO: This is needed to cater for older versions of google-auth + # Make this test unconditional once the minimum supported version of + # google-auth becomes 2.23.0 or higher. + google_auth_major, google_auth_minor = [ + int(part) for part in google.auth.__version__.split(".")[0:2] + ] + if google_auth_major > 2 or (google_auth_major == 2 and google_auth_minor >= 23): + credentials = ga_credentials.AnonymousCredentials() + credentials._universe_domain = "foo.com" + # Test the case when there is a universe mismatch from the credentials. + client = client_class(transport=transport_class(credentials=credentials)) + with pytest.raises(ValueError) as excinfo: + client._validate_universe_domain() + assert ( + str(excinfo.value) + == "The configured universe domain (googleapis.com) does not match the universe domain found in the credentials (foo.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default." + ) + + # Test the case when there is a universe mismatch from the client. + # + # TODO: Make this test unconditional once the minimum supported version of + # google-api-core becomes 2.15.0 or higher. + api_core_major, api_core_minor = [ + int(part) for part in api_core_version.__version__.split(".")[0:2] + ] + if api_core_major > 2 or (api_core_major == 2 and api_core_minor >= 15): + client = client_class( + client_options={"universe_domain": "bar.com"}, + transport=transport_class( + credentials=ga_credentials.AnonymousCredentials(), + ), + ) + with pytest.raises(ValueError) as excinfo: + client._validate_universe_domain() + assert ( + str(excinfo.value) + == "The configured universe domain (bar.com) does not match the universe domain found in the credentials (googleapis.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default." + ) + + # Test that ValueError is raised if universe_domain is provided via client options and credentials is None + with pytest.raises(ValueError): + client._compare_universes("foo.bar", None) + + @pytest.mark.parametrize( "client_class,transport_name", [ @@ -211,12 +471,14 @@ def test_subscriber_client_get_transport_class(): ], ) @mock.patch.object( - SubscriberClient, "DEFAULT_ENDPOINT", modify_default_endpoint(SubscriberClient) + SubscriberClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SubscriberClient), ) @mock.patch.object( SubscriberAsyncClient, - "DEFAULT_ENDPOINT", - modify_default_endpoint(SubscriberAsyncClient), + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SubscriberAsyncClient), ) def test_subscriber_client_client_options( client_class, transport_class, transport_name @@ -258,7 +520,9 @@ def test_subscriber_client_client_options( patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, @@ -288,15 +552,23 @@ def test_subscriber_client_client_options( # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has # unsupported value. with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): + with pytest.raises(MutualTLSChannelError) as excinfo: client = client_class(transport=transport_name) + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" + ) # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. with mock.patch.dict( os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} ): - with pytest.raises(ValueError): + with pytest.raises(ValueError) as excinfo: client = client_class(transport=transport_name) + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) # Check the case quota_project_id is provided options = client_options.ClientOptions(quota_project_id="octopus") @@ -306,7 +578,9 @@ def test_subscriber_client_client_options( patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id="octopus", @@ -324,7 +598,9 @@ def test_subscriber_client_client_options( patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, @@ -356,12 +632,14 @@ def test_subscriber_client_client_options( ], ) @mock.patch.object( - SubscriberClient, "DEFAULT_ENDPOINT", modify_default_endpoint(SubscriberClient) + SubscriberClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SubscriberClient), ) @mock.patch.object( SubscriberAsyncClient, - "DEFAULT_ENDPOINT", - modify_default_endpoint(SubscriberAsyncClient), + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SubscriberAsyncClient), ) @mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) def test_subscriber_client_mtls_env_auto( @@ -384,7 +662,9 @@ def test_subscriber_client_mtls_env_auto( if use_client_cert_env == "false": expected_client_cert_source = None - expected_host = client.DEFAULT_ENDPOINT + expected_host = client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ) else: expected_client_cert_source = client_cert_source_callback expected_host = client.DEFAULT_MTLS_ENDPOINT @@ -416,7 +696,9 @@ def test_subscriber_client_mtls_env_auto( return_value=client_cert_source_callback, ): if use_client_cert_env == "false": - expected_host = client.DEFAULT_ENDPOINT + expected_host = client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ) expected_client_cert_source = None else: expected_host = client.DEFAULT_MTLS_ENDPOINT @@ -450,7 +732,9 @@ def test_subscriber_client_mtls_env_auto( patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, @@ -536,6 +820,113 @@ def test_subscriber_client_get_mtls_endpoint_and_cert_source(client_class): assert api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT assert cert_source == mock_client_cert_source + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError) as excinfo: + client_class.get_mtls_endpoint_and_cert_source() + + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" + ) + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError) as excinfo: + client_class.get_mtls_endpoint_and_cert_source() + + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) + + +@pytest.mark.parametrize("client_class", [SubscriberClient, SubscriberAsyncClient]) +@mock.patch.object( + SubscriberClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SubscriberClient), +) +@mock.patch.object( + SubscriberAsyncClient, + "_DEFAULT_ENDPOINT_TEMPLATE", + modify_default_endpoint_template(SubscriberAsyncClient), +) +def test_subscriber_client_client_api_endpoint(client_class): + mock_client_cert_source = client_cert_source_callback + api_override = "foo.com" + default_universe = SubscriberClient._DEFAULT_UNIVERSE + default_endpoint = SubscriberClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=default_universe + ) + mock_universe = "bar.com" + mock_endpoint = SubscriberClient._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=mock_universe + ) + + # If ClientOptions.api_endpoint is set and GOOGLE_API_USE_CLIENT_CERTIFICATE="true", + # use ClientOptions.api_endpoint as the api endpoint regardless. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}): + with mock.patch( + "google.auth.transport.requests.AuthorizedSession.configure_mtls_channel" + ): + options = client_options.ClientOptions( + client_cert_source=mock_client_cert_source, api_endpoint=api_override + ) + client = client_class( + client_options=options, + credentials=ga_credentials.AnonymousCredentials(), + ) + assert client.api_endpoint == api_override + + # If ClientOptions.api_endpoint is not set and GOOGLE_API_USE_MTLS_ENDPOINT="never", + # use the _DEFAULT_ENDPOINT_TEMPLATE populated with GDU as the api endpoint. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + client = client_class(credentials=ga_credentials.AnonymousCredentials()) + assert client.api_endpoint == default_endpoint + + # If ClientOptions.api_endpoint is not set and GOOGLE_API_USE_MTLS_ENDPOINT="always", + # use the DEFAULT_MTLS_ENDPOINT as the api endpoint. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + client = client_class(credentials=ga_credentials.AnonymousCredentials()) + assert client.api_endpoint == client_class.DEFAULT_MTLS_ENDPOINT + + # If ClientOptions.api_endpoint is not set, GOOGLE_API_USE_MTLS_ENDPOINT="auto" (default), + # GOOGLE_API_USE_CLIENT_CERTIFICATE="false" (default), default cert source doesn't exist, + # and ClientOptions.universe_domain="bar.com", + # use the _DEFAULT_ENDPOINT_TEMPLATE populated with universe domain as the api endpoint. + options = client_options.ClientOptions() + universe_exists = hasattr(options, "universe_domain") + if universe_exists: + options = client_options.ClientOptions(universe_domain=mock_universe) + client = client_class( + client_options=options, credentials=ga_credentials.AnonymousCredentials() + ) + else: + client = client_class( + client_options=options, credentials=ga_credentials.AnonymousCredentials() + ) + assert client.api_endpoint == ( + mock_endpoint if universe_exists else default_endpoint + ) + assert client.universe_domain == ( + mock_universe if universe_exists else default_universe + ) + + # If ClientOptions does not have a universe domain attribute and GOOGLE_API_USE_MTLS_ENDPOINT="never", + # use the _DEFAULT_ENDPOINT_TEMPLATE populated with GDU as the api endpoint. + options = client_options.ClientOptions() + if hasattr(options, "universe_domain"): + delattr(options, "universe_domain") + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + client = client_class( + client_options=options, credentials=ga_credentials.AnonymousCredentials() + ) + assert client.api_endpoint == default_endpoint + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -562,7 +953,9 @@ def test_subscriber_client_client_options_scopes( patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=["1", "2"], client_cert_source_for_mtls=None, quota_project_id=None, @@ -597,7 +990,9 @@ def test_subscriber_client_client_options_credentials_file( patched.assert_called_once_with( credentials=None, credentials_file="credentials.json", - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, @@ -650,7 +1045,9 @@ def test_subscriber_client_create_channel_credentials_file( patched.assert_called_once_with( credentials=None, credentials_file="credentials.json", - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, @@ -1789,7 +2186,7 @@ async def test_list_subscriptions_flattened_error_async(): def test_list_subscriptions_pager(transport_name: str = "grpc"): client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), transport=transport_name, ) @@ -1841,7 +2238,7 @@ def test_list_subscriptions_pager(transport_name: str = "grpc"): def test_list_subscriptions_pages(transport_name: str = "grpc"): client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), transport=transport_name, ) @@ -1885,7 +2282,7 @@ def test_list_subscriptions_pages(transport_name: str = "grpc"): @pytest.mark.asyncio async def test_list_subscriptions_async_pager(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -1937,7 +2334,7 @@ async def test_list_subscriptions_async_pager(): @pytest.mark.asyncio async def test_list_subscriptions_async_pages(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2888,6 +3285,7 @@ def test_pull_flattened_error(): client.pull( pubsub.PullRequest(), subscription="subscription_value", + return_immediately=True, max_messages=1277, ) @@ -2941,6 +3339,7 @@ async def test_pull_flattened_error_async(): await client.pull( pubsub.PullRequest(), subscription="subscription_value", + return_immediately=True, max_messages=1277, ) @@ -3727,7 +4126,7 @@ async def test_list_snapshots_flattened_error_async(): def test_list_snapshots_pager(transport_name: str = "grpc"): client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), transport=transport_name, ) @@ -3777,7 +4176,7 @@ def test_list_snapshots_pager(transport_name: str = "grpc"): def test_list_snapshots_pages(transport_name: str = "grpc"): client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), transport=transport_name, ) @@ -3819,7 +4218,7 @@ def test_list_snapshots_pages(transport_name: str = "grpc"): @pytest.mark.asyncio async def test_list_snapshots_async_pager(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -3869,7 +4268,7 @@ async def test_list_snapshots_async_pager(): @pytest.mark.asyncio async def test_list_snapshots_async_pages(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials, + credentials=ga_credentials.AnonymousCredentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -6991,6 +7390,7 @@ def test_pull_rest_flattened(): # get truthy value for each flattened field mock_args = dict( subscription="subscription_value", + return_immediately=True, max_messages=1277, ) mock_args.update(sample_request) @@ -7029,6 +7429,7 @@ def test_pull_rest_flattened_error(transport: str = "rest"): client.pull( pubsub.PullRequest(), subscription="subscription_value", + return_immediately=True, max_messages=1277, ) @@ -8970,7 +9371,7 @@ def test_credentials_transport_error(): ) # It is an error to provide an api_key and a credential. - options = mock.Mock() + options = client_options.ClientOptions() options.api_key = "api_key" with pytest.raises(ValueError): client = SubscriberClient( @@ -10484,7 +10885,9 @@ def test_api_key_credentials(client_class, transport_class): patched.assert_called_once_with( credentials=mock_cred, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=client._DEFAULT_ENDPOINT_TEMPLATE.format( + UNIVERSE_DOMAIN=client._DEFAULT_UNIVERSE + ), scopes=None, client_cert_source_for_mtls=None, quota_project_id=None, diff --git a/tests/unit/pubsub_v1/publisher/test_publisher_client.py b/tests/unit/pubsub_v1/publisher/test_publisher_client.py index 5d6013654..cc8eda56c 100644 --- a/tests/unit/pubsub_v1/publisher/test_publisher_client.py +++ b/tests/unit/pubsub_v1/publisher/test_publisher_client.py @@ -317,6 +317,32 @@ def test_publish_with_ordering_key_uses_extended_retry_deadline(creds): _assert_retries_equal(batch_commit_retry, expected_retry) +def test_publish_with_ordering_key_with_no_retry(creds): + client = publisher.Client( + credentials=creds, + publisher_options=types.PublisherOptions(enable_message_ordering=True), + ) + + # Use mocks in lieu of the actual batch class. + batch = mock.Mock(spec=client._batch_class) + future = mock.sentinel.future + future.add_done_callback = mock.Mock(spec=["__call__"]) + batch.publish.return_value = future + + topic = "topic/path" + client._set_batch(topic, batch) + + # Actually mock the batch class now. + batch_class = mock.Mock(spec=(), return_value=batch) + client._set_batch_class(batch_class) + + future = client.publish(topic, b"foo", ordering_key="first", retry=None) + assert future is mock.sentinel.future + + # Check the retry settings used for the batch. + batch_class.assert_called_once() + + def test_publish_attrs_bytestring(creds): client = publisher.Client(credentials=creds) @@ -447,20 +473,12 @@ def test_stop(creds): def test_gapic_instance_method(creds): client = publisher.Client(credentials=creds) - transport_mock = mock.Mock(create_topic=mock.sentinel) - fake_create_topic_rpc = mock.Mock() - transport_mock._wrapped_methods = { - transport_mock.create_topic: fake_create_topic_rpc - } - patcher = mock.patch.object(client, "_transport", new=transport_mock) - topic = gapic_types.Topic(name="projects/foo/topics/bar") - - with patcher: + with mock.patch.object(client, "create_topic") as patched: client.create_topic(topic) - assert fake_create_topic_rpc.call_count == 1 - _, args, _ = fake_create_topic_rpc.mock_calls[0] + assert patched.call_count == 1 + _, args, _ = patched.mock_calls[0] assert args[0] == gapic_types.Topic(name="projects/foo/topics/bar") From a28000231e5281bd99b8b8228143d82a9aaf251e Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Fri, 9 Feb 2024 17:38:03 -0500 Subject: [PATCH 026/190] chore(main): release 2.19.4 (#1079) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a31cb5941..318c38f78 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.19.3" + ".": "2.19.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d9f24c15c..d2a7c2051 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.19.4](https://github.com/googleapis/python-pubsub/compare/v2.19.3...v2.19.4) (2024-02-09) + + +### Bug Fixes + +* **diregapic:** S/bazel/bazelisk/ in DIREGAPIC build GitHub action ([#1064](https://github.com/googleapis/python-pubsub/issues/1064)) ([d56ad12](https://github.com/googleapis/python-pubsub/commit/d56ad12f197e9e379d2a4a0a38be108808985c23)) + ## [2.19.3](https://github.com/googleapis/python-pubsub/compare/v2.19.2...v2.19.3) (2024-02-08) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 3feb0e097..34f81704a 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.3" # {x-release-please-version} +__version__ = "2.19.4" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 3feb0e097..34f81704a 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.3" # {x-release-please-version} +__version__ = "2.19.4" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 259b241f7..6cbf881c8 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.19.3" + "version": "2.19.4" }, "snippets": [ { From 3ad9abbb782ab3d290ab79e5461541b7eb6f1b04 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:34:17 -0800 Subject: [PATCH 027/190] build(deps): bump cryptography from 42.0.0 to 42.0.2 in /.kokoro (#1081) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From b16205106acdf8fa6030c95233a1a77c8f7196f7 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Sat, 17 Feb 2024 18:46:40 -0500 Subject: [PATCH 028/190] build(deps): bump cryptography from 42.0.0 to 42.0.2 in .kokoro (#1082) Co-authored-by: Owl Bot --- .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 2aefd0e91..51213ca00 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:97b671488ad548ef783a452a9e1276ac10f144d5ae56d98cc4bf77ba504082b4 -# created: 2024-02-06T03:20:16.660474034Z + digest: sha256:a0c4463fcfd9893fc172a3b3db2b6ac0c7b94ec6ad458c7dcea12d9693615ac3 +# created: 2024-02-17T12:21:23.177926195Z diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 8c11c9f3e..f80bdcd62 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -93,39 +93,39 @@ colorlog==6.7.0 \ # via # gcp-docuploader # nox -cryptography==42.0.0 \ - --hash=sha256:0a68bfcf57a6887818307600c3c0ebc3f62fbb6ccad2240aa21887cda1f8df1b \ - --hash=sha256:146e971e92a6dd042214b537a726c9750496128453146ab0ee8971a0299dc9bd \ - --hash=sha256:14e4b909373bc5bf1095311fa0f7fcabf2d1a160ca13f1e9e467be1ac4cbdf94 \ - --hash=sha256:206aaf42e031b93f86ad60f9f5d9da1b09164f25488238ac1dc488334eb5e221 \ - --hash=sha256:3005166a39b70c8b94455fdbe78d87a444da31ff70de3331cdec2c568cf25b7e \ - --hash=sha256:324721d93b998cb7367f1e6897370644751e5580ff9b370c0a50dc60a2003513 \ - --hash=sha256:33588310b5c886dfb87dba5f013b8d27df7ffd31dc753775342a1e5ab139e59d \ - --hash=sha256:35cf6ed4c38f054478a9df14f03c1169bb14bd98f0b1705751079b25e1cb58bc \ - --hash=sha256:3ca482ea80626048975360c8e62be3ceb0f11803180b73163acd24bf014133a0 \ - --hash=sha256:56ce0c106d5c3fec1038c3cca3d55ac320a5be1b44bf15116732d0bc716979a2 \ - --hash=sha256:5a217bca51f3b91971400890905a9323ad805838ca3fa1e202a01844f485ee87 \ - --hash=sha256:678cfa0d1e72ef41d48993a7be75a76b0725d29b820ff3cfd606a5b2b33fda01 \ - --hash=sha256:69fd009a325cad6fbfd5b04c711a4da563c6c4854fc4c9544bff3088387c77c0 \ - --hash=sha256:6cf9b76d6e93c62114bd19485e5cb003115c134cf9ce91f8ac924c44f8c8c3f4 \ - --hash=sha256:74f18a4c8ca04134d2052a140322002fef535c99cdbc2a6afc18a8024d5c9d5b \ - --hash=sha256:85f759ed59ffd1d0baad296e72780aa62ff8a71f94dc1ab340386a1207d0ea81 \ - --hash=sha256:87086eae86a700307b544625e3ba11cc600c3c0ef8ab97b0fda0705d6db3d4e3 \ - --hash=sha256:8814722cffcfd1fbd91edd9f3451b88a8f26a5fd41b28c1c9193949d1c689dc4 \ - --hash=sha256:8fedec73d590fd30c4e3f0d0f4bc961aeca8390c72f3eaa1a0874d180e868ddf \ - --hash=sha256:9515ea7f596c8092fdc9902627e51b23a75daa2c7815ed5aa8cf4f07469212ec \ - --hash=sha256:988b738f56c665366b1e4bfd9045c3efae89ee366ca3839cd5af53eaa1401bce \ - --hash=sha256:a2a8d873667e4fd2f34aedab02ba500b824692c6542e017075a2efc38f60a4c0 \ - --hash=sha256:bd7cf7a8d9f34cc67220f1195884151426ce616fdc8285df9054bfa10135925f \ - --hash=sha256:bdce70e562c69bb089523e75ef1d9625b7417c6297a76ac27b1b8b1eb51b7d0f \ - --hash=sha256:be14b31eb3a293fc6e6aa2807c8a3224c71426f7c4e3639ccf1a2f3ffd6df8c3 \ - --hash=sha256:be41b0c7366e5549265adf2145135dca107718fa44b6e418dc7499cfff6b4689 \ - --hash=sha256:c310767268d88803b653fffe6d6f2f17bb9d49ffceb8d70aed50ad45ea49ab08 \ - --hash=sha256:c58115384bdcfe9c7f644c72f10f6f42bed7cf59f7b52fe1bf7ae0a622b3a139 \ - --hash=sha256:c640b0ef54138fde761ec99a6c7dc4ce05e80420262c20fa239e694ca371d434 \ - --hash=sha256:ca20550bb590db16223eb9ccc5852335b48b8f597e2f6f0878bbfd9e7314eb17 \ - --hash=sha256:d97aae66b7de41cdf5b12087b5509e4e9805ed6f562406dfcf60e8481a9a28f8 \ - --hash=sha256:e9326ca78111e4c645f7e49cbce4ed2f3f85e17b61a563328c85a5208cf34440 +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 efbaf617bafd6d5f04c0b0f183745547a1e95f44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 13:28:19 -0800 Subject: [PATCH 029/190] build(deps): bump cryptography from 42.0.2 to 42.0.4 in /.kokoro (#1085) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Owl Bot From c659ac777f177e54d7272a8de93fa9f554b15d46 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Thu, 22 Feb 2024 18:33:57 -0500 Subject: [PATCH 030/190] fix: Update system_test_python_versions (#1096) Co-authored-by: Owl Bot --- CONTRIBUTING.rst | 4 ++-- noxfile.py | 2 +- owlbot.py | 2 +- pytest.ini | 4 +++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 03a700296..727b5ec7f 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -143,12 +143,12 @@ Running System Tests $ nox -s system # Run a single system test - $ nox -s system-3.10 -- -k + $ nox -s system-3.12 -- -k .. note:: - System tests are only configured to run under Python 3.10. + System tests are only configured to run under Python 3.12. For expediency, we do not run them in older versions of Python 3. This alone will not run the tests. You'll need to change some local diff --git a/noxfile.py b/noxfile.py index ec63afcb4..ae87830ec 100644 --- a/noxfile.py +++ b/noxfile.py @@ -50,7 +50,7 @@ UNIT_TEST_EXTRAS: List[str] = [] UNIT_TEST_EXTRAS_BY_PYTHON: Dict[str, List[str]] = {} -SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.10"] +SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.12"] SYSTEM_TEST_STANDARD_DEPENDENCIES: List[str] = [ "mock", "pytest", diff --git a/owlbot.py b/owlbot.py index c156909ff..ad76a48ee 100644 --- a/owlbot.py +++ b/owlbot.py @@ -337,7 +337,7 @@ cov_level=100, versions=gcp.common.detect_versions(path="./google", default_first=True), unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"], - system_test_python_versions=["3.10"], + system_test_python_versions=["3.12"], system_test_external_dependencies=["psutil","flaky"], ) s.move(templated_files, excludes=[".coveragerc", ".github/release-please.yml", "README.rst", "docs/index.rst"]) diff --git a/pytest.ini b/pytest.ini index 7308b0871..ce16e4730 100644 --- a/pytest.ini +++ b/pytest.ini @@ -11,4 +11,6 @@ filterwarnings = # Remove warning once https://github.com/googleapis/gapic-generator-python/issues/1938 is fixed ignore:The return_immediately flag is deprecated and should be set to False.:DeprecationWarning # Remove warning once https://github.com/googleapis/gapic-generator-python/issues/1939 is fixed - ignore:get_mtls_endpoint_and_cert_source is deprecated.:DeprecationWarning \ No newline at end of file + ignore:get_mtls_endpoint_and_cert_source is deprecated.:DeprecationWarning + # Remove warning once https://github.com/grpc/grpc/issues/35974 is fixed + ignore:unclosed:ResourceWarning \ No newline at end of file From efcf0c7ea5dccba4f4d83617d7f163c3c3d6d929 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 16:11:19 -0800 Subject: [PATCH 031/190] chore(main): release 2.19.5 (#1097) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 318c38f78..ba120c9f5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.19.4" + ".": "2.19.5" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d2a7c2051..ab1f26d73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.19.5](https://github.com/googleapis/python-pubsub/compare/v2.19.4...v2.19.5) (2024-02-22) + + +### Bug Fixes + +* Update system_test_python_versions ([#1096](https://github.com/googleapis/python-pubsub/issues/1096)) ([c659ac7](https://github.com/googleapis/python-pubsub/commit/c659ac777f177e54d7272a8de93fa9f554b15d46)) + ## [2.19.4](https://github.com/googleapis/python-pubsub/compare/v2.19.3...v2.19.4) (2024-02-09) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 34f81704a..3aa3b013a 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.4" # {x-release-please-version} +__version__ = "2.19.5" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 34f81704a..3aa3b013a 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.4" # {x-release-please-version} +__version__ = "2.19.5" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 6cbf881c8..78988e6a1 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.19.4" + "version": "2.19.5" }, "snippets": [ { From a395d26ed0fffaee8662f988da97dd35c480af4f Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Thu, 22 Feb 2024 20:46:35 -0500 Subject: [PATCH 032/190] fix: Remove LOGGER.exception() line (#1087) --- google/cloud/pubsub_v1/publisher/_batch/thread.py | 1 - 1 file changed, 1 deletion(-) diff --git a/google/cloud/pubsub_v1/publisher/_batch/thread.py b/google/cloud/pubsub_v1/publisher/_batch/thread.py index e872fcf2b..f6436fb7b 100644 --- a/google/cloud/pubsub_v1/publisher/_batch/thread.py +++ b/google/cloud/pubsub_v1/publisher/_batch/thread.py @@ -290,7 +290,6 @@ def _commit(self) -> None: # Failed to publish batch. self._batch_done_callback(batch_transport_succeeded) - _LOGGER.exception("Failed to publish %s messages.", len(self._futures)) return end = time.time() From 2aecd601113386d677ea6faa31f096407926ef48 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 22:38:49 -0500 Subject: [PATCH 033/190] chore(main): release 2.19.6 (#1098) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ba120c9f5..801b4eea6 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.19.5" + ".": "2.19.6" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ab1f26d73..7e4f7a305 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.19.6](https://github.com/googleapis/python-pubsub/compare/v2.19.5...v2.19.6) (2024-02-23) + + +### Bug Fixes + +* Remove LOGGER.exception() line ([#1087](https://github.com/googleapis/python-pubsub/issues/1087)) ([a395d26](https://github.com/googleapis/python-pubsub/commit/a395d26ed0fffaee8662f988da97dd35c480af4f)) + ## [2.19.5](https://github.com/googleapis/python-pubsub/compare/v2.19.4...v2.19.5) (2024-02-22) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 3aa3b013a..10730db30 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.5" # {x-release-please-version} +__version__ = "2.19.6" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 3aa3b013a..10730db30 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.5" # {x-release-please-version} +__version__ = "2.19.6" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 78988e6a1..d426be31a 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.19.5" + "version": "2.19.6" }, "snippets": [ { From 1a5a1342de8736c6a2b1ac63476667f8a02b5bb8 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Sat, 24 Feb 2024 12:54:57 -0800 Subject: [PATCH 034/190] fix(deps): Require `google-api-core>=1.34.1` (#1080) Co-authored-by: Owl Bot Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> --- .../services/publisher/transports/rest.py | 21 +---- .../schema_service/transports/rest.py | 30 ++----- .../services/subscriber/transports/rest.py | 51 ++--------- google/pubsub_v1/types/pubsub.py | 8 +- tests/unit/gapic/pubsub_v1/test_publisher.py | 57 +++--------- .../gapic/pubsub_v1/test_schema_service.py | 60 +++---------- tests/unit/gapic/pubsub_v1/test_subscriber.py | 90 ++++--------------- 7 files changed, 57 insertions(+), 260 deletions(-) diff --git a/google/pubsub_v1/services/publisher/transports/rest.py b/google/pubsub_v1/services/publisher/transports/rest.py index d450cb6eb..2e274efa7 100644 --- a/google/pubsub_v1/services/publisher/transports/rest.py +++ b/google/pubsub_v1/services/publisher/transports/rest.py @@ -536,9 +536,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -547,7 +545,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -630,7 +627,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -713,7 +709,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -799,7 +794,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -885,7 +879,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -973,7 +966,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1061,7 +1053,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1144,9 +1135,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -1155,7 +1144,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1239,9 +1227,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -1250,7 +1236,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) diff --git a/google/pubsub_v1/services/schema_service/transports/rest.py b/google/pubsub_v1/services/schema_service/transports/rest.py index 5e5d7d478..230476a5b 100644 --- a/google/pubsub_v1/services/schema_service/transports/rest.py +++ b/google/pubsub_v1/services/schema_service/transports/rest.py @@ -572,9 +572,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -583,7 +581,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -667,9 +664,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -678,7 +673,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -761,7 +755,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -841,7 +834,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -927,7 +919,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1015,7 +1006,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1101,7 +1091,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1184,9 +1173,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -1195,7 +1182,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1283,9 +1269,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -1294,7 +1278,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1380,9 +1363,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -1391,7 +1372,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) diff --git a/google/pubsub_v1/services/subscriber/transports/rest.py b/google/pubsub_v1/services/subscriber/transports/rest.py index 26dee31f2..f25678965 100644 --- a/google/pubsub_v1/services/subscriber/transports/rest.py +++ b/google/pubsub_v1/services/subscriber/transports/rest.py @@ -652,9 +652,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -663,7 +661,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -745,9 +742,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -756,7 +751,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -849,9 +843,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -860,7 +852,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -943,7 +934,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1020,7 +1010,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1104,7 +1093,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1197,7 +1185,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1283,7 +1270,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1371,7 +1357,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1453,9 +1438,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -1464,7 +1447,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1539,9 +1521,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -1550,7 +1530,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1626,9 +1605,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -1637,7 +1614,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1723,9 +1699,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -1734,7 +1708,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1841,9 +1814,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -1852,7 +1823,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) @@ -1943,9 +1913,7 @@ def __call__( # Jsonify the request body body = json_format.MessageToJson( - transcoded_request["body"], - including_default_value_fields=False, - use_integers_for_enums=True, + transcoded_request["body"], use_integers_for_enums=True ) uri = transcoded_request["uri"] method = transcoded_request["method"] @@ -1954,7 +1922,6 @@ def __call__( query_params = json.loads( json_format.MessageToJson( transcoded_request["query_params"], - including_default_value_fields=False, use_integers_for_enums=True, ) ) diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index b04d5d745..fef8e475f 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -201,8 +201,8 @@ class AwsKinesis(proto.Message): """ class State(proto.Enum): - r"""Possible states for managed ingestion from Amazon Kinesis - Data Streams. + r"""Possible states for ingestion from Amazon Kinesis Data + Streams. Values: STATE_UNSPECIFIED (0): @@ -317,8 +317,8 @@ class Topic(proto.Message): Output only. An output-only field indicating the state of the topic. ingestion_data_source_settings (google.pubsub_v1.types.IngestionDataSourceSettings): - Optional. Settings for managed ingestion from - a data source into this topic. + Optional. Settings for ingestion from a data + source into this topic. """ class State(proto.Enum): diff --git a/tests/unit/gapic/pubsub_v1/test_publisher.py b/tests/unit/gapic/pubsub_v1/test_publisher.py index aca4c59dd..0df65c02d 100644 --- a/tests/unit/gapic/pubsub_v1/test_publisher.py +++ b/tests/unit/gapic/pubsub_v1/test_publisher.py @@ -1738,7 +1738,8 @@ def test_publish_flattened(): # Call the method with a truthy value for each flattened field, # using the keyword arguments to the method. client.publish( - topic="topic_value", messages=[pubsub.PubsubMessage(data=b"data_blob")] + topic="topic_value", + messages=[pubsub.PubsubMessage(data=b"data_blob")], ) # Establish that the underlying call was made with the expected @@ -3803,11 +3804,7 @@ def test_create_topic_rest_required_fields(request_type=pubsub.Topic): request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -4070,11 +4067,7 @@ def test_update_topic_rest_required_fields(request_type=pubsub.UpdateTopicReques request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -4338,11 +4331,7 @@ def test_publish_rest_required_fields(request_type=pubsub.PublishRequest): request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -4619,11 +4608,7 @@ def test_get_topic_rest_required_fields(request_type=pubsub.GetTopicRequest): request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -4880,11 +4865,7 @@ def test_list_topics_rest_required_fields(request_type=pubsub.ListTopicsRequest) request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -5217,11 +5198,7 @@ def test_list_topic_subscriptions_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -5559,11 +5536,7 @@ def test_list_topic_snapshots_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -5890,11 +5863,7 @@ def test_delete_topic_rest_required_fields(request_type=pubsub.DeleteTopicReques request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -6139,11 +6108,7 @@ def test_detach_subscription_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped diff --git a/tests/unit/gapic/pubsub_v1/test_schema_service.py b/tests/unit/gapic/pubsub_v1/test_schema_service.py index e73ef9114..628c8e8e3 100644 --- a/tests/unit/gapic/pubsub_v1/test_schema_service.py +++ b/tests/unit/gapic/pubsub_v1/test_schema_service.py @@ -3983,11 +3983,7 @@ def test_create_schema_rest_required_fields(request_type=gp_schema.CreateSchemaR request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -4267,11 +4263,7 @@ def test_get_schema_rest_required_fields(request_type=schema.GetSchemaRequest): request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -4532,11 +4524,7 @@ def test_list_schemas_rest_required_fields(request_type=schema.ListSchemasReques request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -4871,11 +4859,7 @@ def test_list_schema_revisions_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -5219,11 +5203,7 @@ def test_commit_schema_rest_required_fields(request_type=gp_schema.CommitSchemaR request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -5502,11 +5482,7 @@ def test_rollback_schema_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -5788,11 +5764,7 @@ def test_delete_schema_revision_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -6053,11 +6025,7 @@ def test_delete_schema_rest_required_fields(request_type=schema.DeleteSchemaRequ request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -6304,11 +6272,7 @@ def test_validate_schema_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -6582,11 +6546,7 @@ def test_validate_message_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py index 4ff6a3f27..c5db83486 100644 --- a/tests/unit/gapic/pubsub_v1/test_subscriber.py +++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py @@ -5223,11 +5223,7 @@ def test_create_subscription_rest_required_fields(request_type=pubsub.Subscripti request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -5522,11 +5518,7 @@ def test_get_subscription_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -5803,11 +5795,7 @@ def test_update_subscription_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -6081,11 +6069,7 @@ def test_list_subscriptions_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -6415,11 +6399,7 @@ def test_delete_subscription_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -6669,11 +6649,7 @@ def test_modify_ack_deadline_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -6941,11 +6917,7 @@ def test_acknowledge_rest_required_fields(request_type=pubsub.AcknowledgeRequest request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -7207,11 +7179,7 @@ def test_pull_rest_required_fields(request_type=pubsub.PullRequest): request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -7496,11 +7464,7 @@ def test_modify_push_config_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -7763,11 +7727,7 @@ def test_get_snapshot_rest_required_fields(request_type=pubsub.GetSnapshotReques request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -8026,11 +7986,7 @@ def test_list_snapshots_rest_required_fields(request_type=pubsub.ListSnapshotsRe request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -8366,11 +8322,7 @@ def test_create_snapshot_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -8646,11 +8598,7 @@ def test_update_snapshot_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -8914,11 +8862,7 @@ def test_delete_snapshot_rest_required_fields( request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped @@ -9163,11 +9107,7 @@ def test_seek_rest_required_fields(request_type=pubsub.SeekRequest): request = request_type(**request_init) pb_request = request_type.pb(request) jsonified_request = json.loads( - json_format.MessageToJson( - pb_request, - including_default_value_fields=False, - use_integers_for_enums=False, - ) + json_format.MessageToJson(pb_request, use_integers_for_enums=False) ) # verify fields with default values are dropped From 706eee6489c2a7d1b6bcb22824c7cf4f1b5f22e3 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:50:04 -0500 Subject: [PATCH 035/190] chore(main): release 2.19.7 (#1099) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 801b4eea6..dfab433ad 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.19.6" + ".": "2.19.7" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e4f7a305..5a8131241 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.19.7](https://github.com/googleapis/python-pubsub/compare/v2.19.6...v2.19.7) (2024-02-24) + + +### Bug Fixes + +* **deps:** Require `google-api-core>=1.34.1` ([#1080](https://github.com/googleapis/python-pubsub/issues/1080)) ([1a5a134](https://github.com/googleapis/python-pubsub/commit/1a5a1342de8736c6a2b1ac63476667f8a02b5bb8)) + ## [2.19.6](https://github.com/googleapis/python-pubsub/compare/v2.19.5...v2.19.6) (2024-02-23) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 10730db30..4e7d05e3e 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.6" # {x-release-please-version} +__version__ = "2.19.7" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 10730db30..4e7d05e3e 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.6" # {x-release-please-version} +__version__ = "2.19.7" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index d426be31a..a38cdca96 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.19.6" + "version": "2.19.7" }, "snippets": [ { From dfd7b951d9151b96cba433837ec99f83b11018a8 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 08:44:54 -0800 Subject: [PATCH 036/190] build(deps): bump cryptography from 42.0.2 to 42.0.4 in .kokoro (#1100) Co-authored-by: Owl Bot --- .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 51213ca00..e4e943e02 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 f80bdcd62..bda8e38c4 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 165c983803c48a17141765395cf9ec2e6a7056fa Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:02:40 -0800 Subject: [PATCH 037/190] fix(deps): Exclude google-auth 2.24.0 and 2.25.0 (#1102) Co-authored-by: Owl Bot --- .../generated_samples/snippet_metadata_google.pubsub.v1.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index a38cdca96..d66015ac4 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.19.7" + "version": "0.1.0" }, "snippets": [ { From cdb94a836eca02b31aa6d7bb767506ef7fc87f31 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Tue, 5 Mar 2024 10:21:11 -0500 Subject: [PATCH 038/190] chore(main): release 2.19.8 (#1106) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index dfab433ad..b2b789d39 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.19.7" + ".": "2.19.8" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a8131241..a8f514f5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.19.8](https://github.com/googleapis/python-pubsub/compare/v2.19.7...v2.19.8) (2024-03-05) + + +### Bug Fixes + +* **deps:** Exclude google-auth 2.24.0 and 2.25.0 ([#1102](https://github.com/googleapis/python-pubsub/issues/1102)) ([165c983](https://github.com/googleapis/python-pubsub/commit/165c983803c48a17141765395cf9ec2e6a7056fa)) + ## [2.19.7](https://github.com/googleapis/python-pubsub/compare/v2.19.6...v2.19.7) (2024-02-24) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 4e7d05e3e..58506fff2 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.7" # {x-release-please-version} +__version__ = "2.19.8" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 4e7d05e3e..58506fff2 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.7" # {x-release-please-version} +__version__ = "2.19.8" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index d66015ac4..adc1b81fb 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "0.1.0" + "version": "2.19.8" }, "snippets": [ { From 141a473561bd0e45d3137a02cbefddb454ab3af4 Mon Sep 17 00:00:00 2001 From: Prad Nelluru Date: Tue, 5 Mar 2024 17:02:50 -0500 Subject: [PATCH 039/190] docs(samples): correct type and description of `timeout` parameter in subscriber quickstart (#1051) Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Co-authored-by: Alex Hong <9397363+hongalex@users.noreply.github.com> --- samples/snippets/quickstart/sub.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/quickstart/sub.py b/samples/snippets/quickstart/sub.py index 0900f652d..fd99aac2d 100644 --- a/samples/snippets/quickstart/sub.py +++ b/samples/snippets/quickstart/sub.py @@ -57,7 +57,7 @@ def callback(message: pubsub_v1.subscriber.message.Message) -> None: parser.add_argument("project_id", help="Google Cloud project ID") parser.add_argument("subscription_id", help="Pub/Sub subscription ID") parser.add_argument( - "timeout", default=None, nargs="?", const=1, help="Pub/Sub subscription ID" + "timeout", default=None, type=float, nargs="?", const=1, help="StreamingPull timeout in seconds" ) args = parser.parse_args() From 54041a527398eb0ec5daa97a346ba3202ce349f3 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 5 Mar 2024 14:43:35 -0800 Subject: [PATCH 040/190] feat: Add include_recaptcha_script for as a new action in firewall policies (#1109) Co-authored-by: Owl Bot Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> --- google/pubsub/__init__.py | 2 +- google/pubsub_v1/__init__.py | 2 +- google/pubsub_v1/services/__init__.py | 2 +- google/pubsub_v1/services/publisher/__init__.py | 2 +- google/pubsub_v1/services/publisher/async_client.py | 2 +- google/pubsub_v1/services/publisher/client.py | 2 +- google/pubsub_v1/services/publisher/pagers.py | 2 +- google/pubsub_v1/services/publisher/transports/__init__.py | 2 +- google/pubsub_v1/services/publisher/transports/base.py | 2 +- google/pubsub_v1/services/publisher/transports/grpc.py | 2 +- google/pubsub_v1/services/publisher/transports/grpc_asyncio.py | 2 +- google/pubsub_v1/services/publisher/transports/rest.py | 2 +- google/pubsub_v1/services/schema_service/__init__.py | 2 +- google/pubsub_v1/services/schema_service/async_client.py | 2 +- google/pubsub_v1/services/schema_service/client.py | 2 +- google/pubsub_v1/services/schema_service/pagers.py | 2 +- google/pubsub_v1/services/schema_service/transports/__init__.py | 2 +- google/pubsub_v1/services/schema_service/transports/base.py | 2 +- google/pubsub_v1/services/schema_service/transports/grpc.py | 2 +- .../services/schema_service/transports/grpc_asyncio.py | 2 +- google/pubsub_v1/services/schema_service/transports/rest.py | 2 +- google/pubsub_v1/services/subscriber/__init__.py | 2 +- google/pubsub_v1/services/subscriber/async_client.py | 2 +- google/pubsub_v1/services/subscriber/client.py | 2 +- google/pubsub_v1/services/subscriber/pagers.py | 2 +- google/pubsub_v1/services/subscriber/transports/__init__.py | 2 +- google/pubsub_v1/services/subscriber/transports/base.py | 2 +- google/pubsub_v1/services/subscriber/transports/grpc.py | 2 +- google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py | 2 +- google/pubsub_v1/services/subscriber/transports/rest.py | 2 +- google/pubsub_v1/types/__init__.py | 2 +- google/pubsub_v1/types/pubsub.py | 2 +- google/pubsub_v1/types/schema.py | 2 +- .../pubsub_v1_generated_publisher_create_topic_async.py | 2 +- .../pubsub_v1_generated_publisher_create_topic_sync.py | 2 +- .../pubsub_v1_generated_publisher_delete_topic_async.py | 2 +- .../pubsub_v1_generated_publisher_delete_topic_sync.py | 2 +- .../pubsub_v1_generated_publisher_detach_subscription_async.py | 2 +- .../pubsub_v1_generated_publisher_detach_subscription_sync.py | 2 +- .../pubsub_v1_generated_publisher_get_topic_async.py | 2 +- .../pubsub_v1_generated_publisher_get_topic_sync.py | 2 +- .../pubsub_v1_generated_publisher_list_topic_snapshots_async.py | 2 +- .../pubsub_v1_generated_publisher_list_topic_snapshots_sync.py | 2 +- ...sub_v1_generated_publisher_list_topic_subscriptions_async.py | 2 +- ...bsub_v1_generated_publisher_list_topic_subscriptions_sync.py | 2 +- .../pubsub_v1_generated_publisher_list_topics_async.py | 2 +- .../pubsub_v1_generated_publisher_list_topics_sync.py | 2 +- .../pubsub_v1_generated_publisher_publish_async.py | 2 +- .../pubsub_v1_generated_publisher_publish_sync.py | 2 +- .../pubsub_v1_generated_publisher_update_topic_async.py | 2 +- .../pubsub_v1_generated_publisher_update_topic_sync.py | 2 +- .../pubsub_v1_generated_schema_service_commit_schema_async.py | 2 +- .../pubsub_v1_generated_schema_service_commit_schema_sync.py | 2 +- .../pubsub_v1_generated_schema_service_create_schema_async.py | 2 +- .../pubsub_v1_generated_schema_service_create_schema_sync.py | 2 +- .../pubsub_v1_generated_schema_service_delete_schema_async.py | 2 +- ..._v1_generated_schema_service_delete_schema_revision_async.py | 2 +- ...b_v1_generated_schema_service_delete_schema_revision_sync.py | 2 +- .../pubsub_v1_generated_schema_service_delete_schema_sync.py | 2 +- .../pubsub_v1_generated_schema_service_get_schema_async.py | 2 +- .../pubsub_v1_generated_schema_service_get_schema_sync.py | 2 +- ...b_v1_generated_schema_service_list_schema_revisions_async.py | 2 +- ...ub_v1_generated_schema_service_list_schema_revisions_sync.py | 2 +- .../pubsub_v1_generated_schema_service_list_schemas_async.py | 2 +- .../pubsub_v1_generated_schema_service_list_schemas_sync.py | 2 +- .../pubsub_v1_generated_schema_service_rollback_schema_async.py | 2 +- .../pubsub_v1_generated_schema_service_rollback_schema_sync.py | 2 +- ...pubsub_v1_generated_schema_service_validate_message_async.py | 2 +- .../pubsub_v1_generated_schema_service_validate_message_sync.py | 2 +- .../pubsub_v1_generated_schema_service_validate_schema_async.py | 2 +- .../pubsub_v1_generated_schema_service_validate_schema_sync.py | 2 +- .../pubsub_v1_generated_subscriber_acknowledge_async.py | 2 +- .../pubsub_v1_generated_subscriber_acknowledge_sync.py | 2 +- .../pubsub_v1_generated_subscriber_create_snapshot_async.py | 2 +- .../pubsub_v1_generated_subscriber_create_snapshot_sync.py | 2 +- .../pubsub_v1_generated_subscriber_create_subscription_async.py | 2 +- .../pubsub_v1_generated_subscriber_create_subscription_sync.py | 2 +- .../pubsub_v1_generated_subscriber_delete_snapshot_async.py | 2 +- .../pubsub_v1_generated_subscriber_delete_snapshot_sync.py | 2 +- .../pubsub_v1_generated_subscriber_delete_subscription_async.py | 2 +- .../pubsub_v1_generated_subscriber_delete_subscription_sync.py | 2 +- .../pubsub_v1_generated_subscriber_get_snapshot_async.py | 2 +- .../pubsub_v1_generated_subscriber_get_snapshot_sync.py | 2 +- .../pubsub_v1_generated_subscriber_get_subscription_async.py | 2 +- .../pubsub_v1_generated_subscriber_get_subscription_sync.py | 2 +- .../pubsub_v1_generated_subscriber_list_snapshots_async.py | 2 +- .../pubsub_v1_generated_subscriber_list_snapshots_sync.py | 2 +- .../pubsub_v1_generated_subscriber_list_subscriptions_async.py | 2 +- .../pubsub_v1_generated_subscriber_list_subscriptions_sync.py | 2 +- .../pubsub_v1_generated_subscriber_modify_ack_deadline_async.py | 2 +- .../pubsub_v1_generated_subscriber_modify_ack_deadline_sync.py | 2 +- .../pubsub_v1_generated_subscriber_modify_push_config_async.py | 2 +- .../pubsub_v1_generated_subscriber_modify_push_config_sync.py | 2 +- .../pubsub_v1_generated_subscriber_pull_async.py | 2 +- .../pubsub_v1_generated_subscriber_pull_sync.py | 2 +- .../pubsub_v1_generated_subscriber_seek_async.py | 2 +- .../pubsub_v1_generated_subscriber_seek_sync.py | 2 +- .../pubsub_v1_generated_subscriber_streaming_pull_async.py | 2 +- .../pubsub_v1_generated_subscriber_streaming_pull_sync.py | 2 +- .../pubsub_v1_generated_subscriber_update_snapshot_async.py | 2 +- .../pubsub_v1_generated_subscriber_update_snapshot_sync.py | 2 +- .../pubsub_v1_generated_subscriber_update_subscription_async.py | 2 +- .../pubsub_v1_generated_subscriber_update_subscription_sync.py | 2 +- .../generated_samples/snippet_metadata_google.pubsub.v1.json | 2 +- scripts/fixup_pubsub_v1_keywords.py | 2 +- tests/__init__.py | 2 +- tests/unit/__init__.py | 2 +- tests/unit/gapic/__init__.py | 2 +- tests/unit/gapic/pubsub_v1/__init__.py | 2 +- tests/unit/gapic/pubsub_v1/test_publisher.py | 2 +- tests/unit/gapic/pubsub_v1/test_schema_service.py | 2 +- tests/unit/gapic/pubsub_v1/test_subscriber.py | 2 +- 112 files changed, 112 insertions(+), 112 deletions(-) diff --git a/google/pubsub/__init__.py b/google/pubsub/__init__.py index ebcbb8271..d6d0a00ff 100644 --- a/google/pubsub/__init__.py +++ b/google/pubsub/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/__init__.py b/google/pubsub_v1/__init__.py index 731d03999..61b89e6b1 100644 --- a/google/pubsub_v1/__init__.py +++ b/google/pubsub_v1/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/__init__.py b/google/pubsub_v1/services/__init__.py index 89a37dc92..8f6cf0682 100644 --- a/google/pubsub_v1/services/__init__.py +++ b/google/pubsub_v1/services/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/publisher/__init__.py b/google/pubsub_v1/services/publisher/__init__.py index 105e64dc0..7e1e8e5ce 100644 --- a/google/pubsub_v1/services/publisher/__init__.py +++ b/google/pubsub_v1/services/publisher/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/publisher/async_client.py b/google/pubsub_v1/services/publisher/async_client.py index 730d5c8a5..700c99c02 100644 --- a/google/pubsub_v1/services/publisher/async_client.py +++ b/google/pubsub_v1/services/publisher/async_client.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/publisher/client.py b/google/pubsub_v1/services/publisher/client.py index 37e7410f9..43ccc6df3 100644 --- a/google/pubsub_v1/services/publisher/client.py +++ b/google/pubsub_v1/services/publisher/client.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/publisher/pagers.py b/google/pubsub_v1/services/publisher/pagers.py index 9c4066499..21ae22b8a 100644 --- a/google/pubsub_v1/services/publisher/pagers.py +++ b/google/pubsub_v1/services/publisher/pagers.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/publisher/transports/__init__.py b/google/pubsub_v1/services/publisher/transports/__init__.py index dc172aa02..393b9a55f 100644 --- a/google/pubsub_v1/services/publisher/transports/__init__.py +++ b/google/pubsub_v1/services/publisher/transports/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/publisher/transports/base.py b/google/pubsub_v1/services/publisher/transports/base.py index 6c92dd8f9..47fea83e6 100644 --- a/google/pubsub_v1/services/publisher/transports/base.py +++ b/google/pubsub_v1/services/publisher/transports/base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/publisher/transports/grpc.py b/google/pubsub_v1/services/publisher/transports/grpc.py index ed6410fd4..0fb520404 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc.py +++ b/google/pubsub_v1/services/publisher/transports/grpc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py index c620cd4ae..2d5c0137d 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/publisher/transports/rest.py b/google/pubsub_v1/services/publisher/transports/rest.py index 2e274efa7..4d70c3e64 100644 --- a/google/pubsub_v1/services/publisher/transports/rest.py +++ b/google/pubsub_v1/services/publisher/transports/rest.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/schema_service/__init__.py b/google/pubsub_v1/services/schema_service/__init__.py index 4e9eb0564..570d29e7c 100644 --- a/google/pubsub_v1/services/schema_service/__init__.py +++ b/google/pubsub_v1/services/schema_service/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/schema_service/async_client.py b/google/pubsub_v1/services/schema_service/async_client.py index 44a4916e6..c0eb61075 100644 --- a/google/pubsub_v1/services/schema_service/async_client.py +++ b/google/pubsub_v1/services/schema_service/async_client.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/schema_service/client.py b/google/pubsub_v1/services/schema_service/client.py index d869e4dec..72fbd44c8 100644 --- a/google/pubsub_v1/services/schema_service/client.py +++ b/google/pubsub_v1/services/schema_service/client.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/schema_service/pagers.py b/google/pubsub_v1/services/schema_service/pagers.py index acff3e954..309e57f53 100644 --- a/google/pubsub_v1/services/schema_service/pagers.py +++ b/google/pubsub_v1/services/schema_service/pagers.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/schema_service/transports/__init__.py b/google/pubsub_v1/services/schema_service/transports/__init__.py index 52fa2850b..73976e7fb 100644 --- a/google/pubsub_v1/services/schema_service/transports/__init__.py +++ b/google/pubsub_v1/services/schema_service/transports/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/schema_service/transports/base.py b/google/pubsub_v1/services/schema_service/transports/base.py index 39151e397..835fcd63e 100644 --- a/google/pubsub_v1/services/schema_service/transports/base.py +++ b/google/pubsub_v1/services/schema_service/transports/base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/schema_service/transports/grpc.py b/google/pubsub_v1/services/schema_service/transports/grpc.py index 66b382bfb..ffe490b22 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py index 94a872b56..78c44142e 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/schema_service/transports/rest.py b/google/pubsub_v1/services/schema_service/transports/rest.py index 230476a5b..953e58052 100644 --- a/google/pubsub_v1/services/schema_service/transports/rest.py +++ b/google/pubsub_v1/services/schema_service/transports/rest.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/subscriber/__init__.py b/google/pubsub_v1/services/subscriber/__init__.py index fb0b7dfba..e6994bdf6 100644 --- a/google/pubsub_v1/services/subscriber/__init__.py +++ b/google/pubsub_v1/services/subscriber/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/subscriber/async_client.py b/google/pubsub_v1/services/subscriber/async_client.py index 78ad7c9a2..33d8de056 100644 --- a/google/pubsub_v1/services/subscriber/async_client.py +++ b/google/pubsub_v1/services/subscriber/async_client.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index 74fdb1d28..6b7103852 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/subscriber/pagers.py b/google/pubsub_v1/services/subscriber/pagers.py index 6a68a4c51..94c88b9b3 100644 --- a/google/pubsub_v1/services/subscriber/pagers.py +++ b/google/pubsub_v1/services/subscriber/pagers.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/subscriber/transports/__init__.py b/google/pubsub_v1/services/subscriber/transports/__init__.py index b34bbb687..af60aba8e 100644 --- a/google/pubsub_v1/services/subscriber/transports/__init__.py +++ b/google/pubsub_v1/services/subscriber/transports/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/subscriber/transports/base.py b/google/pubsub_v1/services/subscriber/transports/base.py index cea627d76..34cb97d49 100644 --- a/google/pubsub_v1/services/subscriber/transports/base.py +++ b/google/pubsub_v1/services/subscriber/transports/base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/subscriber/transports/grpc.py b/google/pubsub_v1/services/subscriber/transports/grpc.py index 85a1942af..82c752fa5 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py index b4a73baf4..356acf17c 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/services/subscriber/transports/rest.py b/google/pubsub_v1/services/subscriber/transports/rest.py index f25678965..0b5f2ccc9 100644 --- a/google/pubsub_v1/services/subscriber/transports/rest.py +++ b/google/pubsub_v1/services/subscriber/transports/rest.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/types/__init__.py b/google/pubsub_v1/types/__init__.py index 80135a019..62568bf66 100644 --- a/google/pubsub_v1/types/__init__.py +++ b/google/pubsub_v1/types/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index fef8e475f..b4d823c6f 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/google/pubsub_v1/types/schema.py b/google/pubsub_v1/types/schema.py index 11853406d..9353e4817 100644 --- a/google/pubsub_v1/types/schema.py +++ b/google/pubsub_v1/types/schema.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_async.py index c047ae881..5694b24ef 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_sync.py index bdfbe2973..7de319c67 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_async.py index d134b0614..3d2b74803 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_sync.py index 56e65a6d0..e760eddbd 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_async.py index 9b9015cd9..86508954b 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_sync.py index c2eb69647..af6b45837 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_async.py index 318c0319a..a8bea68c2 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_sync.py index e3c14e052..051b98093 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_async.py index a65fb35db..4eed18b38 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_sync.py index 0627b8277..1e2266757 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_async.py index ab3a07d55..959f1e824 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_sync.py index 93f1020d7..c648b8d1a 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_async.py index 0d2af9b98..dc4a36ef8 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_sync.py index 4399c8350..8d71be398 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_publish_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_publish_async.py index 529acbf00..5c6b63a2d 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_publish_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_publish_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_publish_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_publish_sync.py index b85ad8d42..9af236d44 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_publish_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_publish_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_async.py index 3f913a7dc..d9451b624 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_sync.py index fbac6a928..9b7326c2c 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_async.py index 7d4d0b0f1..b93ec0b5d 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_sync.py index 16be67cd2..c7caa1cbb 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_async.py index a399a7db1..9cdd164f5 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_sync.py index 73f9e407e..ce8619977 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_async.py index d34bd17a1..1bbebfff5 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_async.py index 194ab8033..a35c3af1b 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_sync.py index 6e956dee7..118fe7340 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_sync.py index 766b60396..77dfe887b 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_async.py index 97fa79d87..240df5a2a 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_sync.py index b6882faa9..2d45faebf 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_async.py index 310a32d71..7ea512001 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_sync.py index ce62376fa..6aebac042 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_async.py index 37d67280a..963618fa9 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_sync.py index 831a0f6f1..d7b9e6464 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_async.py index 249c1ad31..b50a08b18 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_sync.py index 977bb10a6..4d83c8cac 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_async.py index add86c6fa..3dcd25490 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_sync.py index 8e2fa2219..4f509d6b3 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_async.py index d53710204..5deb08a44 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_sync.py index dc9add5cf..cc7826792 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_async.py index c2edca707..7fc2967d5 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_sync.py index 1d270cf2f..85b492df0 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_async.py index b314a652e..3f7c691da 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_sync.py index a8936e80c..7738b03da 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_async.py index 488b06838..306218036 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_sync.py index 7aaf8e423..cbb05f62a 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_async.py index c7db1f593..5bea031a7 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_sync.py index 0d66e43e8..01342901b 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_async.py index dafe8fe3b..08a15f4ed 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_sync.py index 8b7d7d35f..1cde73a72 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_async.py index 37fc866a9..40960acc7 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_sync.py index 9fbed39a1..3ee2e9c62 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_async.py index 3c9ac382a..47c739998 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_sync.py index 867b090e8..22832bc89 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_async.py index cd9a50a39..bcc3be15d 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_sync.py index a02b5c9bd..3477e32d3 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_async.py index ade3e5e57..7b00831a3 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_sync.py index fe61e24f5..8e1a96487 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_async.py index 0a6a8ef9e..8e9372292 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_sync.py index 4107a81a8..1d32afec2 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_async.py index cf45c9479..1d88c9590 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_sync.py index f6a48ec60..43ac23e4f 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_pull_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_pull_async.py index 787ebd29a..3e6f26a0b 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_pull_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_pull_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_pull_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_pull_sync.py index 9e009fcc8..950f18cc1 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_pull_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_pull_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_seek_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_seek_async.py index e24c2452c..9038bfd11 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_seek_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_seek_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_seek_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_seek_sync.py index 322673af9..b57af8fb0 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_seek_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_seek_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_async.py index 392550de5..332c12895 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_sync.py index 7ce0e9619..47926a80a 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_async.py index ae1d2c301..833885af5 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_sync.py index 3fd588f43..1c8f8530d 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_async.py index 6e5632e84..5c00a74dc 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_sync.py index eabb9036d..5cc3de6ec 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index adc1b81fb..d66015ac4 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.19.8" + "version": "0.1.0" }, "snippets": [ { diff --git a/scripts/fixup_pubsub_v1_keywords.py b/scripts/fixup_pubsub_v1_keywords.py index d8379e9bb..35262ea8d 100644 --- a/scripts/fixup_pubsub_v1_keywords.py +++ b/scripts/fixup_pubsub_v1_keywords.py @@ -1,6 +1,6 @@ #! /usr/bin/env python3 # -*- coding: utf-8 -*- -# 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. diff --git a/tests/__init__.py b/tests/__init__.py index 89a37dc92..8f6cf0682 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py index 89a37dc92..8f6cf0682 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/tests/unit/gapic/__init__.py b/tests/unit/gapic/__init__.py index 89a37dc92..8f6cf0682 100644 --- a/tests/unit/gapic/__init__.py +++ b/tests/unit/gapic/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/tests/unit/gapic/pubsub_v1/__init__.py b/tests/unit/gapic/pubsub_v1/__init__.py index 89a37dc92..8f6cf0682 100644 --- a/tests/unit/gapic/pubsub_v1/__init__.py +++ b/tests/unit/gapic/pubsub_v1/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/tests/unit/gapic/pubsub_v1/test_publisher.py b/tests/unit/gapic/pubsub_v1/test_publisher.py index 0df65c02d..4aa24c3ff 100644 --- a/tests/unit/gapic/pubsub_v1/test_publisher.py +++ b/tests/unit/gapic/pubsub_v1/test_publisher.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/tests/unit/gapic/pubsub_v1/test_schema_service.py b/tests/unit/gapic/pubsub_v1/test_schema_service.py index 628c8e8e3..03502f54e 100644 --- a/tests/unit/gapic/pubsub_v1/test_schema_service.py +++ b/tests/unit/gapic/pubsub_v1/test_schema_service.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py index c5db83486..1e66e7b70 100644 --- a/tests/unit/gapic/pubsub_v1/test_subscriber.py +++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. From 08c5149b31fe087825aa8b9af53dafbf878462c3 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Tue, 5 Mar 2024 19:04:20 -0500 Subject: [PATCH 041/190] chore(main): release 2.20.0 (#1110) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 12 ++++++++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b2b789d39..d753b65da 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.19.8" + ".": "2.20.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a8f514f5c..bf31d0226 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.20.0](https://github.com/googleapis/python-pubsub/compare/v2.19.8...v2.20.0) (2024-03-05) + + +### Features + +* Add include_recaptcha_script for as a new action in firewall policies ([#1109](https://github.com/googleapis/python-pubsub/issues/1109)) ([54041a5](https://github.com/googleapis/python-pubsub/commit/54041a527398eb0ec5daa97a346ba3202ce349f3)) + + +### Documentation + +* **samples:** Correct type and description of `timeout` parameter in subscriber quickstart ([#1051](https://github.com/googleapis/python-pubsub/issues/1051)) ([141a473](https://github.com/googleapis/python-pubsub/commit/141a473561bd0e45d3137a02cbefddb454ab3af4)) + ## [2.19.8](https://github.com/googleapis/python-pubsub/compare/v2.19.7...v2.19.8) (2024-03-05) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 58506fff2..551f0d2eb 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.8" # {x-release-please-version} +__version__ = "2.20.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 58506fff2..551f0d2eb 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.19.8" # {x-release-please-version} +__version__ = "2.20.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index d66015ac4..2a8aeeec3 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "0.1.0" + "version": "2.20.0" }, "snippets": [ { From 07e427f675464b9aa79c68dede67082529054980 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Wed, 6 Mar 2024 06:26:10 -0500 Subject: [PATCH 042/190] fix: Catch and surface BaseException() (#1108) Co-authored-by: Owl Bot --- .../subscriber/_protocol/streaming_pull_manager.py | 4 ++-- .../subscriber/test_streaming_pull_manager.py | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py index 2f5a31e49..f07db8546 100644 --- a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py +++ b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py @@ -112,7 +112,7 @@ def _wrap_as_exception(maybe_exception: Any) -> BaseException: def _wrap_callback_errors( callback: Callable[["google.cloud.pubsub_v1.subscriber.message.Message"], Any], - on_callback_error: Callable[[Exception], Any], + on_callback_error: Callable[[BaseException], Any], message: "google.cloud.pubsub_v1.subscriber.message.Message", ): """Wraps a user callback so that if an exception occurs the message is @@ -124,7 +124,7 @@ def _wrap_callback_errors( """ try: callback(message) - except Exception as exc: + except BaseException as exc: # Note: the likelihood of this failing is extremely low. This just adds # a message to a queue, so if this doesn't work the world is in an # unrecoverable state and this thread should just bail. diff --git a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py index 1f781b722..278f3e88e 100644 --- a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py +++ b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py @@ -77,9 +77,14 @@ def test__wrap_callback_errors_no_error(): on_callback_error.assert_not_called() -def test__wrap_callback_errors_error(): - callback_error = ValueError("meep") - +@pytest.mark.parametrize( + "callback_error", + [ + (ValueError("ValueError")), + (BaseException("BaseException")), + ], +) +def test__wrap_callback_errors_error(callback_error): msg = mock.create_autospec(message.Message, instance=True) callback = mock.Mock(side_effect=callback_error) on_callback_error = mock.Mock() From c1c6fa506ca2283134fbecbcb96dba8ed244b90b Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Wed, 6 Mar 2024 10:53:36 -0500 Subject: [PATCH 043/190] chore(main): release 2.20.1 (#1114) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d753b65da..f2db52b48 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.20.0" + ".": "2.20.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index bf31d0226..4b3e37d6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.20.1](https://github.com/googleapis/python-pubsub/compare/v2.20.0...v2.20.1) (2024-03-06) + + +### Bug Fixes + +* Catch and surface BaseException() ([#1108](https://github.com/googleapis/python-pubsub/issues/1108)) ([07e427f](https://github.com/googleapis/python-pubsub/commit/07e427f675464b9aa79c68dede67082529054980)) + ## [2.20.0](https://github.com/googleapis/python-pubsub/compare/v2.19.8...v2.20.0) (2024-03-05) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 551f0d2eb..5585b0b1a 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.20.0" # {x-release-please-version} +__version__ = "2.20.1" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 551f0d2eb..5585b0b1a 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.20.0" # {x-release-please-version} +__version__ = "2.20.1" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 2a8aeeec3..34769342c 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.20.0" + "version": "2.20.1" }, "snippets": [ { From 83dc9fff13aa35518fb9b6a73472816da852d975 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Thu, 14 Mar 2024 10:28:23 -0400 Subject: [PATCH 044/190] docs(samples): Add Create Topic with Kinesis IngestionDataSourceSettings Sample (#1120) Co-authored-by: Owl Bot --- samples/snippets/publisher.py | 63 ++++++++++++++++++++++++++++++ samples/snippets/publisher_test.py | 32 +++++++++++++++ samples/snippets/requirements.txt | 2 +- 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/samples/snippets/publisher.py b/samples/snippets/publisher.py index e2c63556c..282bbf4db 100644 --- a/samples/snippets/publisher.py +++ b/samples/snippets/publisher.py @@ -60,6 +60,51 @@ def create_topic(project_id: str, topic_id: str) -> None: # [END pubsub_create_topic] +def create_topic_kinesis_ingestion( + project_id: str, + topic_id: str, + stream_arn: str, + consumer_arn: str, + aws_role_arn: str, + gcp_service_account: str, +) -> None: + """Create a new Pub/Sub topic with AWS Kinesis Ingestion Settings.""" + # [START pubsub_quickstart_create_topic_kinesis_ingestion] + # [START pubsub_create_topic_kinesis_ingestion] + from google.cloud import pubsub_v1 + from google.pubsub_v1.types import Topic + from google.pubsub_v1.types import IngestionDataSourceSettings + + # TODO(developer) + # project_id = "your-project-id" + # topic_id = "your-topic-id" + # stream_arn = "your-stream-arn" + # consumer_arn = "your-consumer-arn" + # aws_role_arn = "your-aws-role-arn" + # gcp_service_account = "your-gcp-service-account" + + publisher = pubsub_v1.PublisherClient() + topic_path = publisher.topic_path(project_id, topic_id) + + request = Topic( + name=topic_path, + ingestion_data_source_settings=IngestionDataSourceSettings( + aws_kinesis=IngestionDataSourceSettings.AwsKinesis( + stream_arn=stream_arn, + consumer_arn=consumer_arn, + aws_role_arn=aws_role_arn, + gcp_service_account=gcp_service_account, + ) + ), + ) + + topic = publisher.create_topic(request=request) + + print(f"Created topic: {topic.name} with AWS Kinesis Ingestion Settings") + # [END pubsub_quickstart_create_topic_kinesis_ingestion] + # [END pubsub_create_topic_kinesis_ingestion] + + def delete_topic(project_id: str, topic_id: str) -> None: """Deletes an existing Pub/Sub topic.""" # [START pubsub_delete_topic] @@ -430,6 +475,15 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: create_parser = subparsers.add_parser("create", help=create_topic.__doc__) create_parser.add_argument("topic_id") + create_topic_kinesis_ingestion_parser = subparsers.add_parser( + "create_kinesis_ingestion", help=create_topic_kinesis_ingestion.__doc__ + ) + create_topic_kinesis_ingestion_parser.add_argument("topic_id") + create_topic_kinesis_ingestion_parser.add_argument("stream_arn") + create_topic_kinesis_ingestion_parser.add_argument("consumer_arn") + create_topic_kinesis_ingestion_parser.add_argument("aws_role_arn") + create_topic_kinesis_ingestion_parser.add_argument("gcp_service_account") + delete_parser = subparsers.add_parser("delete", help=delete_topic.__doc__) delete_parser.add_argument("topic_id") @@ -490,6 +544,15 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: list_topics(args.project_id) elif args.command == "create": create_topic(args.project_id, args.topic_id) + elif args.command == "create_kinesis_ingestion": + create_topic_kinesis_ingestion( + args.project_id, + args.topic_id, + args.stream_arn, + args.consumer_arn, + args.aws_role_arn, + args.gcp_service_account, + ) elif args.command == "delete": delete_topic(args.project_id, args.topic_id) elif args.command == "publish": diff --git a/samples/snippets/publisher_test.py b/samples/snippets/publisher_test.py index 0a6311308..fa31a74cf 100644 --- a/samples/snippets/publisher_test.py +++ b/samples/snippets/publisher_test.py @@ -124,6 +124,38 @@ def test_create( assert f"Created topic: {topic_path}" in out +def test_create_kinesis_ingestion( + publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] +) -> None: + # The scope of `topic_path` is limited to this function. + topic_path = publisher_client.topic_path(PROJECT_ID, TOPIC_ID) + + # Outside of automated CI tests, these values must be of actual AWS resources for the test to pass. + stream_arn = "arn:aws:kinesis:us-west-2:111111111111:stream/fake-stream-name" + consumer_arn = "arn:aws:kinesis:us-west-2:111111111111:stream/fake-stream-name/consumer/consumer-1:1111111111" + aws_role_arn = "arn:aws:iam::111111111111:role/fake-role-name" + gcp_service_account = ( + "fake-service-account@fake-gcp-project.iam.gserviceaccount.com" + ) + + try: + publisher_client.delete_topic(request={"topic": topic_path}) + except NotFound: + pass + + publisher.create_topic_kinesis_ingestion( + PROJECT_ID, + TOPIC_ID, + stream_arn, + consumer_arn, + aws_role_arn, + gcp_service_account, + ) + + out, _ = capsys.readouterr() + assert f"Created topic: {topic_path} with AWS Kinesis Ingestion Settings" in out + + def test_list(topic_path: str, capsys: CaptureFixture[str]) -> None: publisher.list_topics(PROJECT_ID) out, _ = capsys.readouterr() diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 3fb4e0a69..aba41c7d7 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,4 +1,4 @@ -google-cloud-pubsub==2.19.0 +google-cloud-pubsub==2.20.1 avro==1.11.3 protobuf===4.24.4; python_version == '3.7' protobuf==4.25.1; python_version >= '3.8' From e0e2d831da8d17288c3ae8900bea2388ce8758af Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Fri, 15 Mar 2024 13:56:33 -0400 Subject: [PATCH 045/190] docs(samples): Update Topic with Kinesis Ingestion Settings (#1123) Co-authored-by: Owl Bot --- samples/snippets/publisher.py | 69 +++++++++++++++++++++++++++++- samples/snippets/publisher_test.py | 46 ++++++++++++++++++++ 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/samples/snippets/publisher.py b/samples/snippets/publisher.py index 282bbf4db..6453762a3 100644 --- a/samples/snippets/publisher.py +++ b/samples/snippets/publisher.py @@ -69,7 +69,6 @@ def create_topic_kinesis_ingestion( gcp_service_account: str, ) -> None: """Create a new Pub/Sub topic with AWS Kinesis Ingestion Settings.""" - # [START pubsub_quickstart_create_topic_kinesis_ingestion] # [START pubsub_create_topic_kinesis_ingestion] from google.cloud import pubsub_v1 from google.pubsub_v1.types import Topic @@ -101,10 +100,58 @@ def create_topic_kinesis_ingestion( topic = publisher.create_topic(request=request) print(f"Created topic: {topic.name} with AWS Kinesis Ingestion Settings") - # [END pubsub_quickstart_create_topic_kinesis_ingestion] # [END pubsub_create_topic_kinesis_ingestion] +def update_topic_kinesis_ingestion( + project_id: str, + topic_id: str, + stream_arn: str, + consumer_arn: str, + aws_role_arn: str, + gcp_service_account: str, +) -> None: + """Update Pub/Sub topic with AWS Kinesis Ingestion Settings.""" + # [START pubsub_update_topic_kinesis_ingestion] + from google.cloud import pubsub_v1 + from google.pubsub_v1.types import Topic + from google.pubsub_v1.types import IngestionDataSourceSettings + from google.pubsub_v1.types import UpdateTopicRequest + from google.protobuf import field_mask_pb2 + + # TODO(developer) + # project_id = "your-project-id" + # topic_id = "your-topic-id" + # stream_arn = "your-stream-arn" + # consumer_arn = "your-consumer-arn" + # aws_role_arn = "your-aws-role-arn" + # gcp_service_account = "your-gcp-service-account" + + publisher = pubsub_v1.PublisherClient() + topic_path = publisher.topic_path(project_id, topic_id) + + update_request = UpdateTopicRequest( + topic=Topic( + name=topic_path, + ingestion_data_source_settings=IngestionDataSourceSettings( + aws_kinesis=IngestionDataSourceSettings.AwsKinesis( + stream_arn=stream_arn, + consumer_arn=consumer_arn, + aws_role_arn=aws_role_arn, + gcp_service_account=gcp_service_account, + ) + ), + ), + update_mask=field_mask_pb2.FieldMask(paths=["ingestion_data_source_settings"]), + ) + + topic = publisher.update_topic(request=update_request) + print(f"Updated topic: {topic.name} with AWS Kinesis Ingestion Settings") + + +# [END pubsub_update_topic_kinesis_ingestion] + + def delete_topic(project_id: str, topic_id: str) -> None: """Deletes an existing Pub/Sub topic.""" # [START pubsub_delete_topic] @@ -484,6 +531,15 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: create_topic_kinesis_ingestion_parser.add_argument("aws_role_arn") create_topic_kinesis_ingestion_parser.add_argument("gcp_service_account") + update_topic_kinesis_ingestion_parser = subparsers.add_parser( + "update_kinesis_ingestion", help=update_topic_kinesis_ingestion.__doc__ + ) + update_topic_kinesis_ingestion_parser.add_argument("topic_id") + update_topic_kinesis_ingestion_parser.add_argument("stream_arn") + update_topic_kinesis_ingestion_parser.add_argument("consumer_arn") + update_topic_kinesis_ingestion_parser.add_argument("aws_role_arn") + update_topic_kinesis_ingestion_parser.add_argument("gcp_service_account") + delete_parser = subparsers.add_parser("delete", help=delete_topic.__doc__) delete_parser.add_argument("topic_id") @@ -553,6 +609,15 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: args.aws_role_arn, args.gcp_service_account, ) + elif args.command == "update_kinesis_ingestion": + update_topic_kinesis_ingestion( + args.project_id, + args.topic_id, + args.stream_arn, + args.consumer_arn, + args.aws_role_arn, + args.gcp_service_account, + ) elif args.command == "delete": delete_topic(args.project_id, args.topic_id) elif args.command == "publish": diff --git a/samples/snippets/publisher_test.py b/samples/snippets/publisher_test.py index fa31a74cf..1e673f134 100644 --- a/samples/snippets/publisher_test.py +++ b/samples/snippets/publisher_test.py @@ -123,6 +123,9 @@ def test_create( out, _ = capsys.readouterr() assert f"Created topic: {topic_path}" in out + # Clean up resource created for the test. + publisher_client.delete_topic(request={"topic": topic_path}) + def test_create_kinesis_ingestion( publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] @@ -155,6 +158,49 @@ def test_create_kinesis_ingestion( out, _ = capsys.readouterr() assert f"Created topic: {topic_path} with AWS Kinesis Ingestion Settings" in out + # Clean up resource created for the test. + publisher_client.delete_topic(request={"topic": topic_path}) + + +def test_update_kinesis_ingestion( + publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] +) -> None: + # The scope of `topic_path` is limited to this function. + topic_path = publisher_client.topic_path(PROJECT_ID, TOPIC_ID) + + # Outside of automated CI tests, these values must be of actual AWS resources for the test to pass. + stream_arn = "arn:aws:kinesis:us-west-2:111111111111:stream/fake-stream-name" + consumer_arn = "arn:aws:kinesis:us-west-2:111111111111:stream/fake-stream-name/consumer/consumer-1:1111111111" + aws_role_arn = "arn:aws:iam::111111111111:role/fake-role-name" + gcp_service_account = ( + "fake-service-account@fake-gcp-project.iam.gserviceaccount.com" + ) + + try: + publisher_client.delete_topic(request={"topic": topic_path}) + except NotFound: + pass + + publisher.create_topic(PROJECT_ID, TOPIC_ID) + + out, _ = capsys.readouterr() + assert f"Created topic: {topic_path}" in out + + publisher.update_topic_kinesis_ingestion( + PROJECT_ID, + TOPIC_ID, + stream_arn, + consumer_arn, + aws_role_arn, + gcp_service_account, + ) + + out, _ = capsys.readouterr() + assert f"Updated topic: {topic_path} with AWS Kinesis Ingestion Settings" in out + + # Clean up resource created for the test. + publisher_client.delete_topic(request={"topic": topic_path}) + def test_list(topic_path: str, capsys: CaptureFixture[str]) -> None: publisher.list_topics(PROJECT_ID) From b808507187cbbc20c2142e76e607dc6034dca4c0 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 15:04:38 -0700 Subject: [PATCH 046/190] chore(python): update dependencies in /.kokoro (#1126) Co-authored-by: Owl Bot Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> --- .github/.OwlBot.lock.yaml | 4 +- .kokoro/build.sh | 7 -- .kokoro/docker/docs/Dockerfile | 4 + .kokoro/docker/docs/requirements.in | 1 + .kokoro/docker/docs/requirements.txt | 38 +++++++++ .kokoro/requirements.in | 3 +- .kokoro/requirements.txt | 114 ++++++++++++--------------- 7 files changed, 99 insertions(+), 72 deletions(-) create mode 100644 .kokoro/docker/docs/requirements.in create mode 100644 .kokoro/docker/docs/requirements.txt diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index e4e943e02..4bdeef390 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:98f3afd11308259de6e828e37376d18867fd321aba07826e29e4f8d9cab56bad -# created: 2024-02-27T15:56:18.442440378Z + digest: sha256:a8a80fc6456e433df53fc2a0d72ca0345db0ddefb409f1b75b118dfd1babd952 +# created: 2024-03-15T16:25:47.905264637Z diff --git a/.kokoro/build.sh b/.kokoro/build.sh index c26463488..b3d915296 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -33,13 +33,6 @@ export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/service-account.json # Setup project id. export PROJECT_ID=$(cat "${KOKORO_GFILE_DIR}/project-id.json") -# Remove old nox -python3 -m pip uninstall --yes --quiet nox-automation - -# Install nox -python3 -m pip install --upgrade --quiet nox -python3 -m nox --version - # If this is a continuous build, send the test log to the FlakyBot. # See https://github.com/googleapis/repo-automation-bots/tree/main/packages/flakybot. if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"continuous"* ]]; then diff --git a/.kokoro/docker/docs/Dockerfile b/.kokoro/docker/docs/Dockerfile index 8e39a2cc4..bdaf39fe2 100644 --- a/.kokoro/docker/docs/Dockerfile +++ b/.kokoro/docker/docs/Dockerfile @@ -80,4 +80,8 @@ RUN wget -O /tmp/get-pip.py 'https://bootstrap.pypa.io/get-pip.py' \ # Test pip RUN python3 -m pip +# Install build requirements +COPY requirements.txt /requirements.txt +RUN python3 -m pip install --require-hashes -r requirements.txt + CMD ["python3.8"] diff --git a/.kokoro/docker/docs/requirements.in b/.kokoro/docker/docs/requirements.in new file mode 100644 index 000000000..816817c67 --- /dev/null +++ b/.kokoro/docker/docs/requirements.in @@ -0,0 +1 @@ +nox diff --git a/.kokoro/docker/docs/requirements.txt b/.kokoro/docker/docs/requirements.txt new file mode 100644 index 000000000..0e5d70f20 --- /dev/null +++ b/.kokoro/docker/docs/requirements.txt @@ -0,0 +1,38 @@ +# +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: +# +# pip-compile --allow-unsafe --generate-hashes requirements.in +# +argcomplete==3.2.3 \ + --hash=sha256:bf7900329262e481be5a15f56f19736b376df6f82ed27576fa893652c5de6c23 \ + --hash=sha256:c12355e0494c76a2a7b73e3a59b09024ca0ba1e279fb9ed6c1b82d5b74b6a70c + # via nox +colorlog==6.8.2 \ + --hash=sha256:3e3e079a41feb5a1b64f978b5ea4f46040a94f11f0e8bbb8261e3dbbeca64d44 \ + --hash=sha256:4dcbb62368e2800cb3c5abd348da7e53f6c362dda502ec27c560b2e58a66bd33 + # via nox +distlib==0.3.8 \ + --hash=sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784 \ + --hash=sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64 + # via virtualenv +filelock==3.13.1 \ + --hash=sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e \ + --hash=sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c + # via virtualenv +nox==2024.3.2 \ + --hash=sha256:e53514173ac0b98dd47585096a55572fe504fecede58ced708979184d05440be \ + --hash=sha256:f521ae08a15adbf5e11f16cb34e8d0e6ea521e0b92868f684e91677deb974553 + # via -r requirements.in +packaging==24.0 \ + --hash=sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5 \ + --hash=sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9 + # via nox +platformdirs==4.2.0 \ + --hash=sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068 \ + --hash=sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768 + # via virtualenv +virtualenv==20.25.1 \ + --hash=sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a \ + --hash=sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197 + # via nox diff --git a/.kokoro/requirements.in b/.kokoro/requirements.in index ec867d9fd..fff4d9ce0 100644 --- a/.kokoro/requirements.in +++ b/.kokoro/requirements.in @@ -1,5 +1,5 @@ gcp-docuploader -gcp-releasetool>=1.10.5 # required for compatibility with cryptography>=39.x +gcp-releasetool>=2 # required for compatibility with cryptography>=42.x importlib-metadata typing-extensions twine @@ -8,3 +8,4 @@ setuptools nox>=2022.11.21 # required to remove dependency on py charset-normalizer<3 click<8.1.0 +cryptography>=42.0.5 diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index bda8e38c4..dd61f5f32 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -93,40 +93,41 @@ colorlog==6.7.0 \ # via # gcp-docuploader # nox -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 +cryptography==42.0.5 \ + --hash=sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee \ + --hash=sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576 \ + --hash=sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d \ + --hash=sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30 \ + --hash=sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413 \ + --hash=sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb \ + --hash=sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da \ + --hash=sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4 \ + --hash=sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd \ + --hash=sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc \ + --hash=sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8 \ + --hash=sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1 \ + --hash=sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc \ + --hash=sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e \ + --hash=sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8 \ + --hash=sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940 \ + --hash=sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400 \ + --hash=sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7 \ + --hash=sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16 \ + --hash=sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278 \ + --hash=sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74 \ + --hash=sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec \ + --hash=sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1 \ + --hash=sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2 \ + --hash=sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c \ + --hash=sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922 \ + --hash=sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a \ + --hash=sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6 \ + --hash=sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1 \ + --hash=sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e \ + --hash=sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac \ + --hash=sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7 # via + # -r requirements.in # gcp-releasetool # secretstorage distlib==0.3.7 \ @@ -145,9 +146,9 @@ gcp-docuploader==0.6.5 \ --hash=sha256:30221d4ac3e5a2b9c69aa52fdbef68cc3f27d0e6d0d90e220fc024584b8d2318 \ --hash=sha256:b7458ef93f605b9d46a4bf3a8dc1755dad1f31d030c8679edf304e343b347eea # via -r requirements.in -gcp-releasetool==1.16.0 \ - --hash=sha256:27bf19d2e87aaa884096ff941aa3c592c482be3d6a2bfe6f06afafa6af2353e3 \ - --hash=sha256:a316b197a543fd036209d0caba7a8eb4d236d8e65381c80cbc6d7efaa7606d63 +gcp-releasetool==2.0.0 \ + --hash=sha256:3d73480b50ba243f22d7c7ec08b115a30e1c7817c4899781840c26f9c55b8277 \ + --hash=sha256:7aa9fd935ec61e581eb8458ad00823786d91756c25e492f372b2b30962f3c28f # via -r requirements.in google-api-core==2.12.0 \ --hash=sha256:c22e01b1e3c4dcd90998494879612c38d0a3411d1f7b679eb89e2abe3ce1f553 \ @@ -392,29 +393,18 @@ platformdirs==3.11.0 \ --hash=sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3 \ --hash=sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e # via virtualenv -protobuf==3.20.3 \ - --hash=sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7 \ - --hash=sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c \ - --hash=sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2 \ - --hash=sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b \ - --hash=sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050 \ - --hash=sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9 \ - --hash=sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7 \ - --hash=sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454 \ - --hash=sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480 \ - --hash=sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469 \ - --hash=sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c \ - --hash=sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e \ - --hash=sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db \ - --hash=sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905 \ - --hash=sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b \ - --hash=sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86 \ - --hash=sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4 \ - --hash=sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402 \ - --hash=sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7 \ - --hash=sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4 \ - --hash=sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99 \ - --hash=sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee +protobuf==4.25.3 \ + --hash=sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4 \ + --hash=sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8 \ + --hash=sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c \ + --hash=sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d \ + --hash=sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4 \ + --hash=sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa \ + --hash=sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c \ + --hash=sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019 \ + --hash=sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9 \ + --hash=sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c \ + --hash=sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2 # via # gcp-docuploader # gcp-releasetool @@ -518,7 +508,7 @@ zipp==3.17.0 \ # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: -setuptools==68.2.2 \ - --hash=sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87 \ - --hash=sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a +setuptools==69.2.0 \ + --hash=sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e \ + --hash=sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c # via -r requirements.in From bb6296665739818281672843cbdb4031c66b2284 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:09:48 -0700 Subject: [PATCH 047/190] chore(main): release 2.20.2 (#1121) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f2db52b48..e51d046b0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.20.1" + ".": "2.20.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b3e37d6c..dd6bc4bb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.20.2](https://github.com/googleapis/python-pubsub/compare/v2.20.1...v2.20.2) (2024-03-15) + + +### Documentation + +* **samples:** Add Create Topic with Kinesis IngestionDataSourceSettings Sample ([#1120](https://github.com/googleapis/python-pubsub/issues/1120)) ([83dc9ff](https://github.com/googleapis/python-pubsub/commit/83dc9fff13aa35518fb9b6a73472816da852d975)) +* **samples:** Update Topic with Kinesis Ingestion Settings ([#1123](https://github.com/googleapis/python-pubsub/issues/1123)) ([e0e2d83](https://github.com/googleapis/python-pubsub/commit/e0e2d831da8d17288c3ae8900bea2388ce8758af)) + ## [2.20.1](https://github.com/googleapis/python-pubsub/compare/v2.20.0...v2.20.1) (2024-03-06) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 5585b0b1a..4c1787c53 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.20.1" # {x-release-please-version} +__version__ = "2.20.2" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 5585b0b1a..4c1787c53 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.20.1" # {x-release-please-version} +__version__ = "2.20.2" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 34769342c..11e0f952b 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.20.1" + "version": "2.20.2" }, "snippets": [ { From bdac3ee4dfc26f9a161ed72acbeb3475f67701c5 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:58:33 -0700 Subject: [PATCH 048/190] chore(python): add requirements for docs build (#1125) Co-authored-by: Owl Bot Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> --- .github/.OwlBot.lock.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 4bdeef390..50d6cbf43 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -15,3 +15,4 @@ docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest digest: sha256:a8a80fc6456e433df53fc2a0d72ca0345db0ddefb409f1b75b118dfd1babd952 # created: 2024-03-15T16:25:47.905264637Z + From 2b35e6557e16885ea8e6d4695b8dd3f3bd960eab Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Sat, 16 Mar 2024 08:49:05 -0700 Subject: [PATCH 049/190] chore: remove nox uninstall/reinstall from python build.sh template (#1124) Co-authored-by: Owl Bot Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> --- .github/.OwlBot.lock.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 50d6cbf43..4bdeef390 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -15,4 +15,3 @@ docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest digest: sha256:a8a80fc6456e433df53fc2a0d72ca0345db0ddefb409f1b75b118dfd1babd952 # created: 2024-03-15T16:25:47.905264637Z - From e3bc89eaa51337c93144d6c3100486353d494ad9 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:07:09 -0400 Subject: [PATCH 050/190] docs(samples): Update Region Tags (#1128) --- samples/snippets/publisher.py | 46 +++++++++++++++--------------- samples/snippets/publisher_test.py | 8 +++--- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/samples/snippets/publisher.py b/samples/snippets/publisher.py index 6453762a3..73afc8c97 100644 --- a/samples/snippets/publisher.py +++ b/samples/snippets/publisher.py @@ -60,7 +60,7 @@ def create_topic(project_id: str, topic_id: str) -> None: # [END pubsub_create_topic] -def create_topic_kinesis_ingestion( +def create_topic_with_kinesis_ingestion( project_id: str, topic_id: str, stream_arn: str, @@ -69,7 +69,7 @@ def create_topic_kinesis_ingestion( gcp_service_account: str, ) -> None: """Create a new Pub/Sub topic with AWS Kinesis Ingestion Settings.""" - # [START pubsub_create_topic_kinesis_ingestion] + # [START pubsub_create_topic_with_kinesis_ingestion] from google.cloud import pubsub_v1 from google.pubsub_v1.types import Topic from google.pubsub_v1.types import IngestionDataSourceSettings @@ -100,10 +100,10 @@ def create_topic_kinesis_ingestion( topic = publisher.create_topic(request=request) print(f"Created topic: {topic.name} with AWS Kinesis Ingestion Settings") - # [END pubsub_create_topic_kinesis_ingestion] + # [END pubsub_create_topic_with_kinesis_ingestion] -def update_topic_kinesis_ingestion( +def update_topic_type( project_id: str, topic_id: str, stream_arn: str, @@ -112,7 +112,7 @@ def update_topic_kinesis_ingestion( gcp_service_account: str, ) -> None: """Update Pub/Sub topic with AWS Kinesis Ingestion Settings.""" - # [START pubsub_update_topic_kinesis_ingestion] + # [START pubsub_update_topic_type] from google.cloud import pubsub_v1 from google.pubsub_v1.types import Topic from google.pubsub_v1.types import IngestionDataSourceSettings @@ -149,7 +149,7 @@ def update_topic_kinesis_ingestion( print(f"Updated topic: {topic.name} with AWS Kinesis Ingestion Settings") -# [END pubsub_update_topic_kinesis_ingestion] +# [END pubsub_update_topic_type] def delete_topic(project_id: str, topic_id: str) -> None: @@ -522,23 +522,23 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: create_parser = subparsers.add_parser("create", help=create_topic.__doc__) create_parser.add_argument("topic_id") - create_topic_kinesis_ingestion_parser = subparsers.add_parser( - "create_kinesis_ingestion", help=create_topic_kinesis_ingestion.__doc__ + create_topic_with_kinesis_ingestion_parser = subparsers.add_parser( + "create_kinesis_ingestion", help=create_topic_with_kinesis_ingestion.__doc__ ) - create_topic_kinesis_ingestion_parser.add_argument("topic_id") - create_topic_kinesis_ingestion_parser.add_argument("stream_arn") - create_topic_kinesis_ingestion_parser.add_argument("consumer_arn") - create_topic_kinesis_ingestion_parser.add_argument("aws_role_arn") - create_topic_kinesis_ingestion_parser.add_argument("gcp_service_account") - - update_topic_kinesis_ingestion_parser = subparsers.add_parser( - "update_kinesis_ingestion", help=update_topic_kinesis_ingestion.__doc__ + create_topic_with_kinesis_ingestion_parser.add_argument("topic_id") + create_topic_with_kinesis_ingestion_parser.add_argument("stream_arn") + create_topic_with_kinesis_ingestion_parser.add_argument("consumer_arn") + create_topic_with_kinesis_ingestion_parser.add_argument("aws_role_arn") + create_topic_with_kinesis_ingestion_parser.add_argument("gcp_service_account") + + update_topic_type_parser = subparsers.add_parser( + "update_kinesis_ingestion", help=update_topic_type.__doc__ ) - update_topic_kinesis_ingestion_parser.add_argument("topic_id") - update_topic_kinesis_ingestion_parser.add_argument("stream_arn") - update_topic_kinesis_ingestion_parser.add_argument("consumer_arn") - update_topic_kinesis_ingestion_parser.add_argument("aws_role_arn") - update_topic_kinesis_ingestion_parser.add_argument("gcp_service_account") + update_topic_type_parser.add_argument("topic_id") + update_topic_type_parser.add_argument("stream_arn") + update_topic_type_parser.add_argument("consumer_arn") + update_topic_type_parser.add_argument("aws_role_arn") + update_topic_type_parser.add_argument("gcp_service_account") delete_parser = subparsers.add_parser("delete", help=delete_topic.__doc__) delete_parser.add_argument("topic_id") @@ -601,7 +601,7 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: elif args.command == "create": create_topic(args.project_id, args.topic_id) elif args.command == "create_kinesis_ingestion": - create_topic_kinesis_ingestion( + create_topic_with_kinesis_ingestion( args.project_id, args.topic_id, args.stream_arn, @@ -610,7 +610,7 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: args.gcp_service_account, ) elif args.command == "update_kinesis_ingestion": - update_topic_kinesis_ingestion( + update_topic_type( args.project_id, args.topic_id, args.stream_arn, diff --git a/samples/snippets/publisher_test.py b/samples/snippets/publisher_test.py index 1e673f134..adb015e8a 100644 --- a/samples/snippets/publisher_test.py +++ b/samples/snippets/publisher_test.py @@ -127,7 +127,7 @@ def test_create( publisher_client.delete_topic(request={"topic": topic_path}) -def test_create_kinesis_ingestion( +def test_create_topic_with_kinesis_ingestion( publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] ) -> None: # The scope of `topic_path` is limited to this function. @@ -146,7 +146,7 @@ def test_create_kinesis_ingestion( except NotFound: pass - publisher.create_topic_kinesis_ingestion( + publisher.create_topic_with_kinesis_ingestion( PROJECT_ID, TOPIC_ID, stream_arn, @@ -162,7 +162,7 @@ def test_create_kinesis_ingestion( publisher_client.delete_topic(request={"topic": topic_path}) -def test_update_kinesis_ingestion( +def test_update_topic_type( publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] ) -> None: # The scope of `topic_path` is limited to this function. @@ -186,7 +186,7 @@ def test_update_kinesis_ingestion( out, _ = capsys.readouterr() assert f"Created topic: {topic_path}" in out - publisher.update_topic_kinesis_ingestion( + publisher.update_topic_type( PROJECT_ID, TOPIC_ID, stream_arn, From 3f50187731fa81571a04ed8e587fa97ebba6c33d Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 13:46:17 -0700 Subject: [PATCH 051/190] chore(main): release 2.20.3 (#1129) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e51d046b0..bee47555f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.20.2" + ".": "2.20.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index dd6bc4bb1..017401665 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.20.3](https://github.com/googleapis/python-pubsub/compare/v2.20.2...v2.20.3) (2024-03-21) + + +### Documentation + +* **samples:** Update Region Tags ([#1128](https://github.com/googleapis/python-pubsub/issues/1128)) ([e3bc89e](https://github.com/googleapis/python-pubsub/commit/e3bc89eaa51337c93144d6c3100486353d494ad9)) + ## [2.20.2](https://github.com/googleapis/python-pubsub/compare/v2.20.1...v2.20.2) (2024-03-15) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 4c1787c53..12b6c6e02 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.20.2" # {x-release-please-version} +__version__ = "2.20.3" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 4c1787c53..12b6c6e02 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.20.2" # {x-release-please-version} +__version__ = "2.20.3" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 11e0f952b..2611ce81e 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.20.2" + "version": "2.20.3" }, "snippets": [ { From cfcca733cd905c97f0aa4f6e57b227aa118fb88d Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 22 Mar 2024 14:36:54 -0700 Subject: [PATCH 052/190] chore: Update gapic-generator-python to v1.16.1 (#1130) Co-authored-by: Owl Bot --- .../snippet_metadata_google.pubsub.v1.json | 2 +- tests/unit/gapic/pubsub_v1/test_publisher.py | 507 ++++++++++- .../gapic/pubsub_v1/test_schema_service.py | 567 +++++++++++- tests/unit/gapic/pubsub_v1/test_subscriber.py | 841 +++++++++++++++++- 4 files changed, 1846 insertions(+), 71 deletions(-) diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 2611ce81e..d66015ac4 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.20.3" + "version": "0.1.0" }, "snippets": [ { diff --git a/tests/unit/gapic/pubsub_v1/test_publisher.py b/tests/unit/gapic/pubsub_v1/test_publisher.py index 4aa24c3ff..20cecf1c9 100644 --- a/tests/unit/gapic/pubsub_v1/test_publisher.py +++ b/tests/unit/gapic/pubsub_v1/test_publisher.py @@ -1117,7 +1117,8 @@ def test_create_topic(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.Topic() + request = pubsub.Topic() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Topic) @@ -1143,6 +1144,59 @@ def test_create_topic_empty_call(): assert args[0] == pubsub.Topic() +def test_create_topic_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.Topic( + name="name_value", + kms_key_name="kms_key_name_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_topic), "__call__") as call: + client.create_topic(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.Topic( + name="name_value", + kms_key_name="kms_key_name_value", + ) + + +@pytest.mark.asyncio +async def test_create_topic_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_topic), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Topic( + name="name_value", + kms_key_name="kms_key_name_value", + satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, + ) + ) + response = await client.create_topic() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.Topic() + + @pytest.mark.asyncio async def test_create_topic_async( transport: str = "grpc_asyncio", request_type=pubsub.Topic @@ -1172,7 +1226,8 @@ async def test_create_topic_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.Topic() + request = pubsub.Topic() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Topic) @@ -1357,7 +1412,8 @@ def test_update_topic(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.UpdateTopicRequest() + request = pubsub.UpdateTopicRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Topic) @@ -1383,6 +1439,53 @@ def test_update_topic_empty_call(): assert args[0] == pubsub.UpdateTopicRequest() +def test_update_topic_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.UpdateTopicRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_topic), "__call__") as call: + client.update_topic(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.UpdateTopicRequest() + + +@pytest.mark.asyncio +async def test_update_topic_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_topic), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Topic( + name="name_value", + kms_key_name="kms_key_name_value", + satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, + ) + ) + response = await client.update_topic() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.UpdateTopicRequest() + + @pytest.mark.asyncio async def test_update_topic_async( transport: str = "grpc_asyncio", request_type=pubsub.UpdateTopicRequest @@ -1412,7 +1515,8 @@ async def test_update_topic_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.UpdateTopicRequest() + request = pubsub.UpdateTopicRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Topic) @@ -1604,7 +1708,8 @@ def test_publish(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.PublishRequest() + request = pubsub.PublishRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.PublishResponse) @@ -1627,6 +1732,54 @@ def test_publish_empty_call(): assert args[0] == pubsub.PublishRequest() +def test_publish_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.PublishRequest( + topic="topic_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.publish), "__call__") as call: + client.publish(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.PublishRequest( + topic="topic_value", + ) + + +@pytest.mark.asyncio +async def test_publish_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.publish), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.PublishResponse( + message_ids=["message_ids_value"], + ) + ) + response = await client.publish() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.PublishRequest() + + @pytest.mark.asyncio async def test_publish_async( transport: str = "grpc_asyncio", request_type=pubsub.PublishRequest @@ -1653,7 +1806,8 @@ async def test_publish_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.PublishRequest() + request = pubsub.PublishRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.PublishResponse) @@ -1849,7 +2003,8 @@ def test_get_topic(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.GetTopicRequest() + request = pubsub.GetTopicRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Topic) @@ -1875,6 +2030,57 @@ def test_get_topic_empty_call(): assert args[0] == pubsub.GetTopicRequest() +def test_get_topic_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.GetTopicRequest( + topic="topic_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_topic), "__call__") as call: + client.get_topic(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.GetTopicRequest( + topic="topic_value", + ) + + +@pytest.mark.asyncio +async def test_get_topic_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_topic), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Topic( + name="name_value", + kms_key_name="kms_key_name_value", + satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, + ) + ) + response = await client.get_topic() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.GetTopicRequest() + + @pytest.mark.asyncio async def test_get_topic_async( transport: str = "grpc_asyncio", request_type=pubsub.GetTopicRequest @@ -1904,7 +2110,8 @@ async def test_get_topic_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.GetTopicRequest() + request = pubsub.GetTopicRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Topic) @@ -2086,7 +2293,8 @@ def test_list_topics(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListTopicsRequest() + request = pubsub.ListTopicsRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListTopicsPager) @@ -2109,6 +2317,56 @@ def test_list_topics_empty_call(): assert args[0] == pubsub.ListTopicsRequest() +def test_list_topics_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.ListTopicsRequest( + project="project_value", + page_token="page_token_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_topics), "__call__") as call: + client.list_topics(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.ListTopicsRequest( + project="project_value", + page_token="page_token_value", + ) + + +@pytest.mark.asyncio +async def test_list_topics_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_topics), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.ListTopicsResponse( + next_page_token="next_page_token_value", + ) + ) + response = await client.list_topics() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.ListTopicsRequest() + + @pytest.mark.asyncio async def test_list_topics_async( transport: str = "grpc_asyncio", request_type=pubsub.ListTopicsRequest @@ -2135,7 +2393,8 @@ async def test_list_topics_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListTopicsRequest() + request = pubsub.ListTopicsRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListTopicsAsyncPager) @@ -2511,7 +2770,8 @@ def test_list_topic_subscriptions(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListTopicSubscriptionsRequest() + request = pubsub.ListTopicSubscriptionsRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListTopicSubscriptionsPager) @@ -2537,6 +2797,61 @@ def test_list_topic_subscriptions_empty_call(): assert args[0] == pubsub.ListTopicSubscriptionsRequest() +def test_list_topic_subscriptions_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.ListTopicSubscriptionsRequest( + topic="topic_value", + page_token="page_token_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_topic_subscriptions), "__call__" + ) as call: + client.list_topic_subscriptions(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.ListTopicSubscriptionsRequest( + topic="topic_value", + page_token="page_token_value", + ) + + +@pytest.mark.asyncio +async def test_list_topic_subscriptions_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_topic_subscriptions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.ListTopicSubscriptionsResponse( + subscriptions=["subscriptions_value"], + next_page_token="next_page_token_value", + ) + ) + response = await client.list_topic_subscriptions() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.ListTopicSubscriptionsRequest() + + @pytest.mark.asyncio async def test_list_topic_subscriptions_async( transport: str = "grpc_asyncio", request_type=pubsub.ListTopicSubscriptionsRequest @@ -2566,7 +2881,8 @@ async def test_list_topic_subscriptions_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListTopicSubscriptionsRequest() + request = pubsub.ListTopicSubscriptionsRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListTopicSubscriptionsAsyncPager) @@ -2959,7 +3275,8 @@ def test_list_topic_snapshots(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListTopicSnapshotsRequest() + request = pubsub.ListTopicSnapshotsRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListTopicSnapshotsPager) @@ -2985,6 +3302,61 @@ def test_list_topic_snapshots_empty_call(): assert args[0] == pubsub.ListTopicSnapshotsRequest() +def test_list_topic_snapshots_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.ListTopicSnapshotsRequest( + topic="topic_value", + page_token="page_token_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_topic_snapshots), "__call__" + ) as call: + client.list_topic_snapshots(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.ListTopicSnapshotsRequest( + topic="topic_value", + page_token="page_token_value", + ) + + +@pytest.mark.asyncio +async def test_list_topic_snapshots_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_topic_snapshots), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.ListTopicSnapshotsResponse( + snapshots=["snapshots_value"], + next_page_token="next_page_token_value", + ) + ) + response = await client.list_topic_snapshots() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.ListTopicSnapshotsRequest() + + @pytest.mark.asyncio async def test_list_topic_snapshots_async( transport: str = "grpc_asyncio", request_type=pubsub.ListTopicSnapshotsRequest @@ -3014,7 +3386,8 @@ async def test_list_topic_snapshots_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListTopicSnapshotsRequest() + request = pubsub.ListTopicSnapshotsRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListTopicSnapshotsAsyncPager) @@ -3402,7 +3775,8 @@ def test_delete_topic(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DeleteTopicRequest() + request = pubsub.DeleteTopicRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert response is None @@ -3424,6 +3798,50 @@ def test_delete_topic_empty_call(): assert args[0] == pubsub.DeleteTopicRequest() +def test_delete_topic_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.DeleteTopicRequest( + topic="topic_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_topic), "__call__") as call: + client.delete_topic(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.DeleteTopicRequest( + topic="topic_value", + ) + + +@pytest.mark.asyncio +async def test_delete_topic_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_topic), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + response = await client.delete_topic() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.DeleteTopicRequest() + + @pytest.mark.asyncio async def test_delete_topic_async( transport: str = "grpc_asyncio", request_type=pubsub.DeleteTopicRequest @@ -3446,7 +3864,8 @@ async def test_delete_topic_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DeleteTopicRequest() + request = pubsub.DeleteTopicRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert response is None @@ -3624,7 +4043,8 @@ def test_detach_subscription(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DetachSubscriptionRequest() + request = pubsub.DetachSubscriptionRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.DetachSubscriptionResponse) @@ -3648,6 +4068,56 @@ def test_detach_subscription_empty_call(): assert args[0] == pubsub.DetachSubscriptionRequest() +def test_detach_subscription_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.DetachSubscriptionRequest( + subscription="subscription_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.detach_subscription), "__call__" + ) as call: + client.detach_subscription(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.DetachSubscriptionRequest( + subscription="subscription_value", + ) + + +@pytest.mark.asyncio +async def test_detach_subscription_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.detach_subscription), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.DetachSubscriptionResponse() + ) + response = await client.detach_subscription() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.DetachSubscriptionRequest() + + @pytest.mark.asyncio async def test_detach_subscription_async( transport: str = "grpc_asyncio", request_type=pubsub.DetachSubscriptionRequest @@ -3674,7 +4144,8 @@ async def test_detach_subscription_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DetachSubscriptionRequest() + request = pubsub.DetachSubscriptionRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.DetachSubscriptionResponse) diff --git a/tests/unit/gapic/pubsub_v1/test_schema_service.py b/tests/unit/gapic/pubsub_v1/test_schema_service.py index 03502f54e..5de9c6c45 100644 --- a/tests/unit/gapic/pubsub_v1/test_schema_service.py +++ b/tests/unit/gapic/pubsub_v1/test_schema_service.py @@ -1156,7 +1156,8 @@ def test_create_schema(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == gp_schema.CreateSchemaRequest() + request = gp_schema.CreateSchemaRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, gp_schema.Schema) @@ -1182,6 +1183,59 @@ def test_create_schema_empty_call(): assert args[0] == gp_schema.CreateSchemaRequest() +def test_create_schema_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = gp_schema.CreateSchemaRequest( + parent="parent_value", + schema_id="schema_id_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_schema), "__call__") as call: + client.create_schema(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == gp_schema.CreateSchemaRequest( + parent="parent_value", + schema_id="schema_id_value", + ) + + +@pytest.mark.asyncio +async def test_create_schema_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_schema), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gp_schema.Schema( + name="name_value", + type_=gp_schema.Schema.Type.PROTOCOL_BUFFER, + definition="definition_value", + revision_id="revision_id_value", + ) + ) + response = await client.create_schema() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == gp_schema.CreateSchemaRequest() + + @pytest.mark.asyncio async def test_create_schema_async( transport: str = "grpc_asyncio", request_type=gp_schema.CreateSchemaRequest @@ -1211,7 +1265,8 @@ async def test_create_schema_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == gp_schema.CreateSchemaRequest() + request = gp_schema.CreateSchemaRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, gp_schema.Schema) @@ -1416,7 +1471,8 @@ def test_get_schema(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == schema.GetSchemaRequest() + request = schema.GetSchemaRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, schema.Schema) @@ -1442,6 +1498,57 @@ def test_get_schema_empty_call(): assert args[0] == schema.GetSchemaRequest() +def test_get_schema_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = schema.GetSchemaRequest( + name="name_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_schema), "__call__") as call: + client.get_schema(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == schema.GetSchemaRequest( + name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_schema_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_schema), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + schema.Schema( + name="name_value", + type_=schema.Schema.Type.PROTOCOL_BUFFER, + definition="definition_value", + revision_id="revision_id_value", + ) + ) + response = await client.get_schema() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == schema.GetSchemaRequest() + + @pytest.mark.asyncio async def test_get_schema_async( transport: str = "grpc_asyncio", request_type=schema.GetSchemaRequest @@ -1471,7 +1578,8 @@ async def test_get_schema_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == schema.GetSchemaRequest() + request = schema.GetSchemaRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, schema.Schema) @@ -1653,7 +1761,8 @@ def test_list_schemas(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == schema.ListSchemasRequest() + request = schema.ListSchemasRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListSchemasPager) @@ -1676,6 +1785,56 @@ def test_list_schemas_empty_call(): assert args[0] == schema.ListSchemasRequest() +def test_list_schemas_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = schema.ListSchemasRequest( + parent="parent_value", + page_token="page_token_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_schemas), "__call__") as call: + client.list_schemas(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == schema.ListSchemasRequest( + parent="parent_value", + page_token="page_token_value", + ) + + +@pytest.mark.asyncio +async def test_list_schemas_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_schemas), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + schema.ListSchemasResponse( + next_page_token="next_page_token_value", + ) + ) + response = await client.list_schemas() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == schema.ListSchemasRequest() + + @pytest.mark.asyncio async def test_list_schemas_async( transport: str = "grpc_asyncio", request_type=schema.ListSchemasRequest @@ -1702,7 +1861,8 @@ async def test_list_schemas_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == schema.ListSchemasRequest() + request = schema.ListSchemasRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListSchemasAsyncPager) @@ -2077,7 +2237,8 @@ def test_list_schema_revisions(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == schema.ListSchemaRevisionsRequest() + request = schema.ListSchemaRevisionsRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListSchemaRevisionsPager) @@ -2102,6 +2263,60 @@ def test_list_schema_revisions_empty_call(): assert args[0] == schema.ListSchemaRevisionsRequest() +def test_list_schema_revisions_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = schema.ListSchemaRevisionsRequest( + name="name_value", + page_token="page_token_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_schema_revisions), "__call__" + ) as call: + client.list_schema_revisions(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == schema.ListSchemaRevisionsRequest( + name="name_value", + page_token="page_token_value", + ) + + +@pytest.mark.asyncio +async def test_list_schema_revisions_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_schema_revisions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + schema.ListSchemaRevisionsResponse( + next_page_token="next_page_token_value", + ) + ) + response = await client.list_schema_revisions() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == schema.ListSchemaRevisionsRequest() + + @pytest.mark.asyncio async def test_list_schema_revisions_async( transport: str = "grpc_asyncio", request_type=schema.ListSchemaRevisionsRequest @@ -2130,7 +2345,8 @@ async def test_list_schema_revisions_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == schema.ListSchemaRevisionsRequest() + request = schema.ListSchemaRevisionsRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListSchemaRevisionsAsyncPager) @@ -2522,7 +2738,8 @@ def test_commit_schema(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == gp_schema.CommitSchemaRequest() + request = gp_schema.CommitSchemaRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, gp_schema.Schema) @@ -2548,6 +2765,57 @@ def test_commit_schema_empty_call(): assert args[0] == gp_schema.CommitSchemaRequest() +def test_commit_schema_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = gp_schema.CommitSchemaRequest( + name="name_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.commit_schema), "__call__") as call: + client.commit_schema(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == gp_schema.CommitSchemaRequest( + name="name_value", + ) + + +@pytest.mark.asyncio +async def test_commit_schema_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.commit_schema), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gp_schema.Schema( + name="name_value", + type_=gp_schema.Schema.Type.PROTOCOL_BUFFER, + definition="definition_value", + revision_id="revision_id_value", + ) + ) + response = await client.commit_schema() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == gp_schema.CommitSchemaRequest() + + @pytest.mark.asyncio async def test_commit_schema_async( transport: str = "grpc_asyncio", request_type=gp_schema.CommitSchemaRequest @@ -2577,7 +2845,8 @@ async def test_commit_schema_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == gp_schema.CommitSchemaRequest() + request = gp_schema.CommitSchemaRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, gp_schema.Schema) @@ -2772,7 +3041,8 @@ def test_rollback_schema(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == schema.RollbackSchemaRequest() + request = schema.RollbackSchemaRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, schema.Schema) @@ -2798,6 +3068,59 @@ def test_rollback_schema_empty_call(): assert args[0] == schema.RollbackSchemaRequest() +def test_rollback_schema_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = schema.RollbackSchemaRequest( + name="name_value", + revision_id="revision_id_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.rollback_schema), "__call__") as call: + client.rollback_schema(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == schema.RollbackSchemaRequest( + name="name_value", + revision_id="revision_id_value", + ) + + +@pytest.mark.asyncio +async def test_rollback_schema_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.rollback_schema), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + schema.Schema( + name="name_value", + type_=schema.Schema.Type.PROTOCOL_BUFFER, + definition="definition_value", + revision_id="revision_id_value", + ) + ) + response = await client.rollback_schema() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == schema.RollbackSchemaRequest() + + @pytest.mark.asyncio async def test_rollback_schema_async( transport: str = "grpc_asyncio", request_type=schema.RollbackSchemaRequest @@ -2827,7 +3150,8 @@ async def test_rollback_schema_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == schema.RollbackSchemaRequest() + request = schema.RollbackSchemaRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, schema.Schema) @@ -3024,7 +3348,8 @@ def test_delete_schema_revision(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == schema.DeleteSchemaRevisionRequest() + request = schema.DeleteSchemaRevisionRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, schema.Schema) @@ -3052,6 +3377,63 @@ def test_delete_schema_revision_empty_call(): assert args[0] == schema.DeleteSchemaRevisionRequest() +def test_delete_schema_revision_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = schema.DeleteSchemaRevisionRequest( + name="name_value", + revision_id="revision_id_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_schema_revision), "__call__" + ) as call: + client.delete_schema_revision(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == schema.DeleteSchemaRevisionRequest( + name="name_value", + revision_id="revision_id_value", + ) + + +@pytest.mark.asyncio +async def test_delete_schema_revision_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_schema_revision), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + schema.Schema( + name="name_value", + type_=schema.Schema.Type.PROTOCOL_BUFFER, + definition="definition_value", + revision_id="revision_id_value", + ) + ) + response = await client.delete_schema_revision() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == schema.DeleteSchemaRevisionRequest() + + @pytest.mark.asyncio async def test_delete_schema_revision_async( transport: str = "grpc_asyncio", request_type=schema.DeleteSchemaRevisionRequest @@ -3083,7 +3465,8 @@ async def test_delete_schema_revision_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == schema.DeleteSchemaRevisionRequest() + request = schema.DeleteSchemaRevisionRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, schema.Schema) @@ -3281,7 +3664,8 @@ def test_delete_schema(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == schema.DeleteSchemaRequest() + request = schema.DeleteSchemaRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert response is None @@ -3303,6 +3687,50 @@ def test_delete_schema_empty_call(): assert args[0] == schema.DeleteSchemaRequest() +def test_delete_schema_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = schema.DeleteSchemaRequest( + name="name_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_schema), "__call__") as call: + client.delete_schema(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == schema.DeleteSchemaRequest( + name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_schema_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_schema), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + response = await client.delete_schema() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == schema.DeleteSchemaRequest() + + @pytest.mark.asyncio async def test_delete_schema_async( transport: str = "grpc_asyncio", request_type=schema.DeleteSchemaRequest @@ -3325,7 +3753,8 @@ async def test_delete_schema_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == schema.DeleteSchemaRequest() + request = schema.DeleteSchemaRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert response is None @@ -3501,7 +3930,8 @@ def test_validate_schema(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == gp_schema.ValidateSchemaRequest() + request = gp_schema.ValidateSchemaRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, gp_schema.ValidateSchemaResponse) @@ -3523,6 +3953,52 @@ def test_validate_schema_empty_call(): assert args[0] == gp_schema.ValidateSchemaRequest() +def test_validate_schema_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = gp_schema.ValidateSchemaRequest( + parent="parent_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.validate_schema), "__call__") as call: + client.validate_schema(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == gp_schema.ValidateSchemaRequest( + parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_validate_schema_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.validate_schema), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gp_schema.ValidateSchemaResponse() + ) + response = await client.validate_schema() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == gp_schema.ValidateSchemaRequest() + + @pytest.mark.asyncio async def test_validate_schema_async( transport: str = "grpc_asyncio", request_type=gp_schema.ValidateSchemaRequest @@ -3547,7 +4023,8 @@ async def test_validate_schema_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == gp_schema.ValidateSchemaRequest() + request = gp_schema.ValidateSchemaRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, gp_schema.ValidateSchemaResponse) @@ -3737,7 +4214,8 @@ def test_validate_message(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == schema.ValidateMessageRequest() + request = schema.ValidateMessageRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, schema.ValidateMessageResponse) @@ -3759,6 +4237,54 @@ def test_validate_message_empty_call(): assert args[0] == schema.ValidateMessageRequest() +def test_validate_message_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = schema.ValidateMessageRequest( + parent="parent_value", + name="name_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.validate_message), "__call__") as call: + client.validate_message(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == schema.ValidateMessageRequest( + parent="parent_value", + name="name_value", + ) + + +@pytest.mark.asyncio +async def test_validate_message_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.validate_message), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + schema.ValidateMessageResponse() + ) + response = await client.validate_message() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == schema.ValidateMessageRequest() + + @pytest.mark.asyncio async def test_validate_message_async( transport: str = "grpc_asyncio", request_type=schema.ValidateMessageRequest @@ -3783,7 +4309,8 @@ async def test_validate_message_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == schema.ValidateMessageRequest() + request = schema.ValidateMessageRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, schema.ValidateMessageResponse) diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py index 1e66e7b70..50e02b52d 100644 --- a/tests/unit/gapic/pubsub_v1/test_subscriber.py +++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py @@ -1128,7 +1128,8 @@ def test_create_subscription(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.Subscription() + request = pubsub.Subscription() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Subscription) @@ -1161,6 +1162,70 @@ def test_create_subscription_empty_call(): assert args[0] == pubsub.Subscription() +def test_create_subscription_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.Subscription( + name="name_value", + topic="topic_value", + filter="filter_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_subscription), "__call__" + ) as call: + client.create_subscription(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.Subscription( + name="name_value", + topic="topic_value", + filter="filter_value", + ) + + +@pytest.mark.asyncio +async def test_create_subscription_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_subscription), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Subscription( + name="name_value", + topic="topic_value", + ack_deadline_seconds=2066, + retain_acked_messages=True, + enable_message_ordering=True, + filter="filter_value", + detached=True, + enable_exactly_once_delivery=True, + state=pubsub.Subscription.State.ACTIVE, + ) + ) + response = await client.create_subscription() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.Subscription() + + @pytest.mark.asyncio async def test_create_subscription_async( transport: str = "grpc_asyncio", request_type=pubsub.Subscription @@ -1197,7 +1262,8 @@ async def test_create_subscription_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.Subscription() + request = pubsub.Subscription() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Subscription) @@ -1430,7 +1496,8 @@ def test_get_subscription(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.GetSubscriptionRequest() + request = pubsub.GetSubscriptionRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Subscription) @@ -1461,6 +1528,62 @@ def test_get_subscription_empty_call(): assert args[0] == pubsub.GetSubscriptionRequest() +def test_get_subscription_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.GetSubscriptionRequest( + subscription="subscription_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_subscription), "__call__") as call: + client.get_subscription(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.GetSubscriptionRequest( + subscription="subscription_value", + ) + + +@pytest.mark.asyncio +async def test_get_subscription_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_subscription), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Subscription( + name="name_value", + topic="topic_value", + ack_deadline_seconds=2066, + retain_acked_messages=True, + enable_message_ordering=True, + filter="filter_value", + detached=True, + enable_exactly_once_delivery=True, + state=pubsub.Subscription.State.ACTIVE, + ) + ) + response = await client.get_subscription() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.GetSubscriptionRequest() + + @pytest.mark.asyncio async def test_get_subscription_async( transport: str = "grpc_asyncio", request_type=pubsub.GetSubscriptionRequest @@ -1495,7 +1618,8 @@ async def test_get_subscription_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.GetSubscriptionRequest() + request = pubsub.GetSubscriptionRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Subscription) @@ -1692,7 +1816,8 @@ def test_update_subscription(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.UpdateSubscriptionRequest() + request = pubsub.UpdateSubscriptionRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Subscription) @@ -1725,6 +1850,62 @@ def test_update_subscription_empty_call(): assert args[0] == pubsub.UpdateSubscriptionRequest() +def test_update_subscription_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.UpdateSubscriptionRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_subscription), "__call__" + ) as call: + client.update_subscription(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.UpdateSubscriptionRequest() + + +@pytest.mark.asyncio +async def test_update_subscription_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_subscription), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Subscription( + name="name_value", + topic="topic_value", + ack_deadline_seconds=2066, + retain_acked_messages=True, + enable_message_ordering=True, + filter="filter_value", + detached=True, + enable_exactly_once_delivery=True, + state=pubsub.Subscription.State.ACTIVE, + ) + ) + response = await client.update_subscription() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.UpdateSubscriptionRequest() + + @pytest.mark.asyncio async def test_update_subscription_async( transport: str = "grpc_asyncio", request_type=pubsub.UpdateSubscriptionRequest @@ -1761,7 +1942,8 @@ async def test_update_subscription_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.UpdateSubscriptionRequest() + request = pubsub.UpdateSubscriptionRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Subscription) @@ -1968,7 +2150,8 @@ def test_list_subscriptions(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListSubscriptionsRequest() + request = pubsub.ListSubscriptionsRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListSubscriptionsPager) @@ -1993,6 +2176,60 @@ def test_list_subscriptions_empty_call(): assert args[0] == pubsub.ListSubscriptionsRequest() +def test_list_subscriptions_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.ListSubscriptionsRequest( + project="project_value", + page_token="page_token_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_subscriptions), "__call__" + ) as call: + client.list_subscriptions(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.ListSubscriptionsRequest( + project="project_value", + page_token="page_token_value", + ) + + +@pytest.mark.asyncio +async def test_list_subscriptions_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_subscriptions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.ListSubscriptionsResponse( + next_page_token="next_page_token_value", + ) + ) + response = await client.list_subscriptions() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.ListSubscriptionsRequest() + + @pytest.mark.asyncio async def test_list_subscriptions_async( transport: str = "grpc_asyncio", request_type=pubsub.ListSubscriptionsRequest @@ -2021,7 +2258,8 @@ async def test_list_subscriptions_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListSubscriptionsRequest() + request = pubsub.ListSubscriptionsRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListSubscriptionsAsyncPager) @@ -2410,7 +2648,8 @@ def test_delete_subscription(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DeleteSubscriptionRequest() + request = pubsub.DeleteSubscriptionRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert response is None @@ -2434,6 +2673,54 @@ def test_delete_subscription_empty_call(): assert args[0] == pubsub.DeleteSubscriptionRequest() +def test_delete_subscription_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.DeleteSubscriptionRequest( + subscription="subscription_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_subscription), "__call__" + ) as call: + client.delete_subscription(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.DeleteSubscriptionRequest( + subscription="subscription_value", + ) + + +@pytest.mark.asyncio +async def test_delete_subscription_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_subscription), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + response = await client.delete_subscription() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.DeleteSubscriptionRequest() + + @pytest.mark.asyncio async def test_delete_subscription_async( transport: str = "grpc_asyncio", request_type=pubsub.DeleteSubscriptionRequest @@ -2458,7 +2745,8 @@ async def test_delete_subscription_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DeleteSubscriptionRequest() + request = pubsub.DeleteSubscriptionRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert response is None @@ -2644,7 +2932,8 @@ def test_modify_ack_deadline(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ModifyAckDeadlineRequest() + request = pubsub.ModifyAckDeadlineRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert response is None @@ -2668,6 +2957,54 @@ def test_modify_ack_deadline_empty_call(): assert args[0] == pubsub.ModifyAckDeadlineRequest() +def test_modify_ack_deadline_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.ModifyAckDeadlineRequest( + subscription="subscription_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.modify_ack_deadline), "__call__" + ) as call: + client.modify_ack_deadline(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.ModifyAckDeadlineRequest( + subscription="subscription_value", + ) + + +@pytest.mark.asyncio +async def test_modify_ack_deadline_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.modify_ack_deadline), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + response = await client.modify_ack_deadline() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.ModifyAckDeadlineRequest() + + @pytest.mark.asyncio async def test_modify_ack_deadline_async( transport: str = "grpc_asyncio", request_type=pubsub.ModifyAckDeadlineRequest @@ -2692,7 +3029,8 @@ async def test_modify_ack_deadline_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ModifyAckDeadlineRequest() + request = pubsub.ModifyAckDeadlineRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert response is None @@ -2896,7 +3234,8 @@ def test_acknowledge(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.AcknowledgeRequest() + request = pubsub.AcknowledgeRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert response is None @@ -2918,6 +3257,50 @@ def test_acknowledge_empty_call(): assert args[0] == pubsub.AcknowledgeRequest() +def test_acknowledge_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.AcknowledgeRequest( + subscription="subscription_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.acknowledge), "__call__") as call: + client.acknowledge(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.AcknowledgeRequest( + subscription="subscription_value", + ) + + +@pytest.mark.asyncio +async def test_acknowledge_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.acknowledge), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + response = await client.acknowledge() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.AcknowledgeRequest() + + @pytest.mark.asyncio async def test_acknowledge_async( transport: str = "grpc_asyncio", request_type=pubsub.AcknowledgeRequest @@ -2940,7 +3323,8 @@ async def test_acknowledge_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.AcknowledgeRequest() + request = pubsub.AcknowledgeRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert response is None @@ -3126,7 +3510,8 @@ def test_pull(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.PullRequest() + request = pubsub.PullRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.PullResponse) @@ -3148,6 +3533,50 @@ def test_pull_empty_call(): assert args[0] == pubsub.PullRequest() +def test_pull_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.PullRequest( + subscription="subscription_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.pull), "__call__") as call: + client.pull(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.PullRequest( + subscription="subscription_value", + ) + + +@pytest.mark.asyncio +async def test_pull_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.pull), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(pubsub.PullResponse()) + response = await client.pull() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.PullRequest() + + @pytest.mark.asyncio async def test_pull_async( transport: str = "grpc_asyncio", request_type=pubsub.PullRequest @@ -3170,7 +3599,8 @@ async def test_pull_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.PullRequest() + request = pubsub.PullRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.PullResponse) @@ -3444,7 +3874,8 @@ def test_modify_push_config(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ModifyPushConfigRequest() + request = pubsub.ModifyPushConfigRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert response is None @@ -3455,14 +3886,62 @@ def test_modify_push_config_empty_call(): # i.e. request == None and no flattened fields passed, work. client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", + transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.modify_push_config), "__call__" + ) as call: + client.modify_push_config() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.ModifyPushConfigRequest() + + +def test_modify_push_config_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.ModifyPushConfigRequest( + subscription="subscription_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.modify_push_config), "__call__" + ) as call: + client.modify_push_config(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.ModifyPushConfigRequest( + subscription="subscription_value", + ) + + +@pytest.mark.asyncio +async def test_modify_push_config_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", ) # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( type(client.transport.modify_push_config), "__call__" ) as call: - client.modify_push_config() + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + response = await client.modify_push_config() call.assert_called() _, args, _ = call.mock_calls[0] assert args[0] == pubsub.ModifyPushConfigRequest() @@ -3492,7 +3971,8 @@ async def test_modify_push_config_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ModifyPushConfigRequest() + request = pubsub.ModifyPushConfigRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert response is None @@ -3689,7 +4169,8 @@ def test_get_snapshot(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.GetSnapshotRequest() + request = pubsub.GetSnapshotRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Snapshot) @@ -3713,6 +4194,55 @@ def test_get_snapshot_empty_call(): assert args[0] == pubsub.GetSnapshotRequest() +def test_get_snapshot_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.GetSnapshotRequest( + snapshot="snapshot_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_snapshot), "__call__") as call: + client.get_snapshot(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.GetSnapshotRequest( + snapshot="snapshot_value", + ) + + +@pytest.mark.asyncio +async def test_get_snapshot_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_snapshot), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Snapshot( + name="name_value", + topic="topic_value", + ) + ) + response = await client.get_snapshot() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.GetSnapshotRequest() + + @pytest.mark.asyncio async def test_get_snapshot_async( transport: str = "grpc_asyncio", request_type=pubsub.GetSnapshotRequest @@ -3740,7 +4270,8 @@ async def test_get_snapshot_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.GetSnapshotRequest() + request = pubsub.GetSnapshotRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Snapshot) @@ -3920,7 +4451,8 @@ def test_list_snapshots(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListSnapshotsRequest() + request = pubsub.ListSnapshotsRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListSnapshotsPager) @@ -3943,6 +4475,56 @@ def test_list_snapshots_empty_call(): assert args[0] == pubsub.ListSnapshotsRequest() +def test_list_snapshots_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.ListSnapshotsRequest( + project="project_value", + page_token="page_token_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_snapshots), "__call__") as call: + client.list_snapshots(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.ListSnapshotsRequest( + project="project_value", + page_token="page_token_value", + ) + + +@pytest.mark.asyncio +async def test_list_snapshots_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_snapshots), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.ListSnapshotsResponse( + next_page_token="next_page_token_value", + ) + ) + response = await client.list_snapshots() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.ListSnapshotsRequest() + + @pytest.mark.asyncio async def test_list_snapshots_async( transport: str = "grpc_asyncio", request_type=pubsub.ListSnapshotsRequest @@ -3969,7 +4551,8 @@ async def test_list_snapshots_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListSnapshotsRequest() + request = pubsub.ListSnapshotsRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListSnapshotsAsyncPager) @@ -4343,7 +4926,8 @@ def test_create_snapshot(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.CreateSnapshotRequest() + request = pubsub.CreateSnapshotRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Snapshot) @@ -4367,6 +4951,57 @@ def test_create_snapshot_empty_call(): assert args[0] == pubsub.CreateSnapshotRequest() +def test_create_snapshot_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.CreateSnapshotRequest( + name="name_value", + subscription="subscription_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_snapshot), "__call__") as call: + client.create_snapshot(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.CreateSnapshotRequest( + name="name_value", + subscription="subscription_value", + ) + + +@pytest.mark.asyncio +async def test_create_snapshot_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_snapshot), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Snapshot( + name="name_value", + topic="topic_value", + ) + ) + response = await client.create_snapshot() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.CreateSnapshotRequest() + + @pytest.mark.asyncio async def test_create_snapshot_async( transport: str = "grpc_asyncio", request_type=pubsub.CreateSnapshotRequest @@ -4394,7 +5029,8 @@ async def test_create_snapshot_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.CreateSnapshotRequest() + request = pubsub.CreateSnapshotRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Snapshot) @@ -4585,7 +5221,8 @@ def test_update_snapshot(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.UpdateSnapshotRequest() + request = pubsub.UpdateSnapshotRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Snapshot) @@ -4609,6 +5246,51 @@ def test_update_snapshot_empty_call(): assert args[0] == pubsub.UpdateSnapshotRequest() +def test_update_snapshot_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.UpdateSnapshotRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_snapshot), "__call__") as call: + client.update_snapshot(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.UpdateSnapshotRequest() + + +@pytest.mark.asyncio +async def test_update_snapshot_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_snapshot), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Snapshot( + name="name_value", + topic="topic_value", + ) + ) + response = await client.update_snapshot() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.UpdateSnapshotRequest() + + @pytest.mark.asyncio async def test_update_snapshot_async( transport: str = "grpc_asyncio", request_type=pubsub.UpdateSnapshotRequest @@ -4636,7 +5318,8 @@ async def test_update_snapshot_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.UpdateSnapshotRequest() + request = pubsub.UpdateSnapshotRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.Snapshot) @@ -4824,7 +5507,8 @@ def test_delete_snapshot(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DeleteSnapshotRequest() + request = pubsub.DeleteSnapshotRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert response is None @@ -4846,6 +5530,50 @@ def test_delete_snapshot_empty_call(): assert args[0] == pubsub.DeleteSnapshotRequest() +def test_delete_snapshot_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.DeleteSnapshotRequest( + snapshot="snapshot_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_snapshot), "__call__") as call: + client.delete_snapshot(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.DeleteSnapshotRequest( + snapshot="snapshot_value", + ) + + +@pytest.mark.asyncio +async def test_delete_snapshot_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_snapshot), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + response = await client.delete_snapshot() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.DeleteSnapshotRequest() + + @pytest.mark.asyncio async def test_delete_snapshot_async( transport: str = "grpc_asyncio", request_type=pubsub.DeleteSnapshotRequest @@ -4868,7 +5596,8 @@ async def test_delete_snapshot_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DeleteSnapshotRequest() + request = pubsub.DeleteSnapshotRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert response is None @@ -5044,7 +5773,8 @@ def test_seek(request_type, transport: str = "grpc"): # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) == 1 _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.SeekRequest() + request = pubsub.SeekRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.SeekResponse) @@ -5066,6 +5796,52 @@ def test_seek_empty_call(): assert args[0] == pubsub.SeekRequest() +def test_seek_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.SeekRequest( + subscription="subscription_value", + snapshot="snapshot_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.seek), "__call__") as call: + client.seek(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.SeekRequest( + subscription="subscription_value", + snapshot="snapshot_value", + ) + + +@pytest.mark.asyncio +async def test_seek_empty_call_async(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.seek), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(pubsub.SeekResponse()) + response = await client.seek() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == pubsub.SeekRequest() + + @pytest.mark.asyncio async def test_seek_async( transport: str = "grpc_asyncio", request_type=pubsub.SeekRequest @@ -5088,7 +5864,8 @@ async def test_seek_async( # Establish that the underlying gRPC stub method was called. assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.SeekRequest() + request = pubsub.SeekRequest() + assert args[0] == request # Establish that the response is the type that we expect. assert isinstance(response, pubsub.SeekResponse) From 4da67441ddab01a173620d8c03bc640271c785c6 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 26 Mar 2024 09:38:06 -0700 Subject: [PATCH 053/190] feat: add custom datetime format for Cloud Storage subscriptions (#1131) Co-authored-by: Owl Bot --- google/pubsub_v1/types/pubsub.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index b4d823c6f..55cda3abd 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -222,7 +222,7 @@ class State(proto.Enum): ``gcp_service_account``. PUBLISH_PERMISSION_DENIED (3): Permission denied encountered while publishing to the topic. - This can happen due to Pub/Sub SA has not been granted the + This can happen if the Pub/Sub SA has not been granted the `appropriate publish permissions `__ STREAM_NOT_FOUND (4): @@ -1394,6 +1394,11 @@ class CloudStorageConfig(proto.Message): See the `object naming requirements `__. Must not end in "/". + filename_datetime_format (str): + Optional. User-provided format string specifying how to + represent datetimes in Cloud Storage filenames. See the + `datetime format + guidance `__. text_config (google.pubsub_v1.types.CloudStorageConfig.TextConfig): Optional. If set, message data will be written to Cloud Storage in text format. @@ -1487,6 +1492,10 @@ class AvroConfig(proto.Message): proto.STRING, number=3, ) + filename_datetime_format: str = proto.Field( + proto.STRING, + number=10, + ) text_config: TextConfig = proto.Field( proto.MESSAGE, number=4, From 8c7e2a9e1b158e2b75dec8c5d78be17ce2dd449b Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Tue, 26 Mar 2024 10:20:39 -0700 Subject: [PATCH 054/190] chore(main): release 2.21.0 (#1132) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index bee47555f..1ff4c05fc 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.20.3" + ".": "2.21.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 017401665..6b648e4ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.21.0](https://github.com/googleapis/python-pubsub/compare/v2.20.3...v2.21.0) (2024-03-26) + + +### Features + +* Add custom datetime format for Cloud Storage subscriptions ([#1131](https://github.com/googleapis/python-pubsub/issues/1131)) ([4da6744](https://github.com/googleapis/python-pubsub/commit/4da67441ddab01a173620d8c03bc640271c785c6)) + ## [2.20.3](https://github.com/googleapis/python-pubsub/compare/v2.20.2...v2.20.3) (2024-03-21) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 12b6c6e02..e546bae05 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.20.3" # {x-release-please-version} +__version__ = "2.21.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 12b6c6e02..e546bae05 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.20.3" # {x-release-please-version} +__version__ = "2.21.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index d66015ac4..b053d68de 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "0.1.0" + "version": "2.21.0" }, "snippets": [ { From 67daf3c64239d22eabe59c3df214057a4e59a39e Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Wed, 3 Apr 2024 23:17:58 -0400 Subject: [PATCH 055/190] fix: Set timeout to infinite for publishing with ordering keys enabled (#1134) --- google/cloud/pubsub_v1/publisher/client.py | 5 +++++ tests/unit/pubsub_v1/publisher/test_publisher_client.py | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/google/cloud/pubsub_v1/publisher/client.py b/google/cloud/pubsub_v1/publisher/client.py index caf9fa180..54b353276 100644 --- a/google/cloud/pubsub_v1/publisher/client.py +++ b/google/cloud/pubsub_v1/publisher/client.py @@ -399,8 +399,13 @@ def on_publish_done(future): transport = self._transport base_retry = transport._wrapped_methods[transport.publish]._retry retry = base_retry.with_deadline(2.0**32) + # timeout needs to be overridden and set to infinite in + # addition to the retry deadline since both determine + # the duration for which retries are attempted. + timeout = 2.0**32 elif retry is not None: retry = retry.with_deadline(2.0**32) + timeout = 2.0**32 # Delegate the publishing to the sequencer. sequencer = self._get_or_create_sequencer(topic, ordering_key) diff --git a/tests/unit/pubsub_v1/publisher/test_publisher_client.py b/tests/unit/pubsub_v1/publisher/test_publisher_client.py index cc8eda56c..460246738 100644 --- a/tests/unit/pubsub_v1/publisher/test_publisher_client.py +++ b/tests/unit/pubsub_v1/publisher/test_publisher_client.py @@ -316,6 +316,10 @@ def test_publish_with_ordering_key_uses_extended_retry_deadline(creds): expected_retry = custom_retry.with_deadline(2.0**32) _assert_retries_equal(batch_commit_retry, expected_retry) + batch_commit_timeout = kwargs["commit_timeout"] + expected_timeout = 2.0**32 + assert batch_commit_timeout == pytest.approx(expected_timeout) + def test_publish_with_ordering_key_with_no_retry(creds): client = publisher.Client( From b9d767d2ccaeb628dfcb4aedf1fb972ff7fc755d Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Wed, 3 Apr 2024 21:02:12 -0700 Subject: [PATCH 056/190] chore(main): release 2.21.1 (#1135) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1ff4c05fc..72e6fd856 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.21.0" + ".": "2.21.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b648e4ee..0854b871b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.21.1](https://github.com/googleapis/python-pubsub/compare/v2.21.0...v2.21.1) (2024-04-04) + + +### Bug Fixes + +* Set timeout to infinite for publishing with ordering keys enabled ([#1134](https://github.com/googleapis/python-pubsub/issues/1134)) ([67daf3c](https://github.com/googleapis/python-pubsub/commit/67daf3c64239d22eabe59c3df214057a4e59a39e)) + ## [2.21.0](https://github.com/googleapis/python-pubsub/compare/v2.20.3...v2.21.0) (2024-03-26) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index e546bae05..e71611fa6 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.21.0" # {x-release-please-version} +__version__ = "2.21.1" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index e546bae05..e71611fa6 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.21.0" # {x-release-please-version} +__version__ = "2.21.1" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index b053d68de..619f0eac4 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.21.0" + "version": "2.21.1" }, "snippets": [ { From cb12fb6c449f5e4927ad2f25ba49b243e65b0d0e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 11:26:05 -0400 Subject: [PATCH 057/190] build(deps): bump idna from 3.4 to 3.7 in /.kokoro (#1139) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Owl Bot From 64422149c06ca2d25bc1f62a572b5145172c4c0b Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 13:16:11 -0400 Subject: [PATCH 058/190] chore(python): bump idna from 3.4 to 3.7 in .kokoro (#1140) Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- .github/.OwlBot.lock.yaml | 4 ++-- .github/auto-label.yaml | 5 +++++ .github/blunderbuss.yml | 15 +++++++++++++-- .kokoro/requirements.txt | 6 +++--- docs/index.rst | 5 +++++ docs/summary_overview.md | 22 ++++++++++++++++++++++ owlbot.py | 1 + 7 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 docs/summary_overview.md diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 4bdeef390..81f87c569 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:a8a80fc6456e433df53fc2a0d72ca0345db0ddefb409f1b75b118dfd1babd952 -# created: 2024-03-15T16:25:47.905264637Z + digest: sha256:5a4c19d17e597b92d786e569be101e636c9c2817731f80a5adec56b2aa8fe070 +# created: 2024-04-12T11:35:58.922854369Z diff --git a/.github/auto-label.yaml b/.github/auto-label.yaml index b2016d119..8b37ee897 100644 --- a/.github/auto-label.yaml +++ b/.github/auto-label.yaml @@ -13,3 +13,8 @@ # limitations under the License. requestsize: enabled: true + +path: + pullrequest: true + paths: + samples: "samples" diff --git a/.github/blunderbuss.yml b/.github/blunderbuss.yml index 992f04dce..3408b580a 100644 --- a/.github/blunderbuss.yml +++ b/.github/blunderbuss.yml @@ -1,6 +1,17 @@ -# Configuration for the Blunderbuss GitHub app. For more info see -# https://github.com/googleapis/repo-automation-bots/tree/main/packages/blunderbuss +# Blunderbuss config +# +# This file controls who is assigned for pull requests and issues. +# Note: This file is autogenerated. To make changes to the assignee +# team, please update `codeowner_team` in `.repo-metadata.json`. assign_issues: - mukund-ananthu + +assign_issues_by: + - labels: + - "samples" + to: + - googleapis/python-samples-reviewers + - mukund-ananthu + assign_prs: - mukund-ananthu diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index dd61f5f32..51f92b8e1 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -252,9 +252,9 @@ 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 +idna==3.7 \ + --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \ + --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0 # via requests importlib-metadata==6.8.0 \ --hash=sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb \ diff --git a/docs/index.rst b/docs/index.rst index 6b3e1583b..daba0c7b3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -41,3 +41,8 @@ For a list of all ``google-cloud-pubsub`` releases: changelog + +.. toctree:: + :hidden: + + summary_overview.md diff --git a/docs/summary_overview.md b/docs/summary_overview.md new file mode 100644 index 000000000..171339711 --- /dev/null +++ b/docs/summary_overview.md @@ -0,0 +1,22 @@ +[ +This is a templated file. Adding content to this file may result in it being +reverted. Instead, if you want to place additional content, create an +"overview_content.md" file in `docs/` directory. The Sphinx tool will +pick up on the content and merge the content. +]: # + +# Google Cloud Pub/Sub API + +Overview of the APIs available for Google Cloud Pub/Sub API. + +## All entries + +Classes, methods and properties & attributes for +Google Cloud Pub/Sub API. + +[classes](https://cloud.google.com/python/docs/reference/pubsub/latest/summary_class.html) + +[methods](https://cloud.google.com/python/docs/reference/pubsub/latest/summary_method.html) + +[properties and +attributes](https://cloud.google.com/python/docs/reference/pubsub/latest/summary_property.html) diff --git a/owlbot.py b/owlbot.py index ad76a48ee..d80434efd 100644 --- a/owlbot.py +++ b/owlbot.py @@ -432,6 +432,7 @@ def mypy_samples(session): "noxfile.py", "--cov=google", "--cov=google/cloud", ) +s.replace(".github/blunderbuss.yml", "googleapis/api-pubsub", "mukund-ananthu") python.py_samples(skip_readmes=True) From 9ed13eb819a5db2169bd30f3decb3aae9d7dbcd7 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Fri, 12 Apr 2024 20:07:49 +0200 Subject: [PATCH 059/190] chore(deps): update all dependencies (#1057) --- samples/snippets/requirements-test.txt | 5 +++-- samples/snippets/requirements.txt | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index 17e317ce3..286a4de5d 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1,6 +1,7 @@ backoff==2.2.1 -pytest==7.4.3 +pytest===7.4.4; python_version == '3.7' +pytest==8.0.0; python_version >= '3.8' mock==5.1.0 flaky==3.7.0 -google-cloud-bigquery==3.14.1 +google-cloud-bigquery==3.17.2 google-cloud-storage==2.14.0 diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index aba41c7d7..8979c92e0 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,5 +1,5 @@ google-cloud-pubsub==2.20.1 avro==1.11.3 protobuf===4.24.4; python_version == '3.7' -protobuf==4.25.1; python_version >= '3.8' +protobuf==4.25.2; python_version >= '3.8' avro==1.11.3 From ff229a5fdd4deaff0ac97c74f313d04b62720ff7 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Fri, 12 Apr 2024 23:42:20 +0200 Subject: [PATCH 060/190] chore(deps): update all dependencies (#1142) Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- samples/snippets/requirements-test.txt | 8 ++++---- samples/snippets/requirements.txt | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index 286a4de5d..ab0de59c1 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1,7 +1,7 @@ backoff==2.2.1 pytest===7.4.4; python_version == '3.7' -pytest==8.0.0; python_version >= '3.8' +pytest==8.1.1; python_version >= '3.8' mock==5.1.0 -flaky==3.7.0 -google-cloud-bigquery==3.17.2 -google-cloud-storage==2.14.0 +flaky==3.8.1 +google-cloud-bigquery==3.20.1 +google-cloud-storage==2.16.0 diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 8979c92e0..5ea08a511 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,4 +1,4 @@ -google-cloud-pubsub==2.20.1 +google-cloud-pubsub==2.21.1 avro==1.11.3 protobuf===4.24.4; python_version == '3.7' protobuf==4.25.2; python_version >= '3.8' From 086dd4660ec56d9ff2d41a32ec0b8e8dc44acc55 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Thu, 30 May 2024 10:18:37 -0400 Subject: [PATCH 061/190] fix: Test failures due to grpcio changes (#1178) --- .../publisher/test_publisher_client.py | 25 ++++++++++++++++--- .../subscriber/test_subscriber_client.py | 25 ++++++++++++++++--- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/tests/unit/pubsub_v1/publisher/test_publisher_client.py b/tests/unit/pubsub_v1/publisher/test_publisher_client.py index 460246738..9db5e0ef8 100644 --- a/tests/unit/pubsub_v1/publisher/test_publisher_client.py +++ b/tests/unit/pubsub_v1/publisher/test_publisher_client.py @@ -133,17 +133,28 @@ def test_init_w_api_endpoint(creds): client_options = {"api_endpoint": "testendpoint.google.com"} client = publisher.Client(client_options=client_options, credentials=creds) + # Behavior to include dns prefix changed in gRPCv1.63 + grpc_major, grpc_minor = [int(part) for part in grpc.__version__.split(".")[0:2]] + if grpc_major > 1 or (grpc_major == 1 and grpc_minor >= 63): + _EXPECTED_TARGET = "dns:///testendpoint.google.com:443" + else: + _EXPECTED_TARGET = "testendpoint.google.com:443" assert (client._transport.grpc_channel._channel.target()).decode( "utf-8" - ) == "testendpoint.google.com:443" + ) == _EXPECTED_TARGET def test_init_w_empty_client_options(creds): client = publisher.Client(client_options={}, credentials=creds) - + # Behavior to include dns prefix changed in gRPCv1.63 + grpc_major, grpc_minor = [int(part) for part in grpc.__version__.split(".")[0:2]] + if grpc_major > 1 or (grpc_major == 1 and grpc_minor >= 63): + _EXPECTED_TARGET = "dns:///pubsub.googleapis.com:443" + else: + _EXPECTED_TARGET = "pubsub.googleapis.com:443" assert (client._transport.grpc_channel._channel.target()).decode( "utf-8" - ) == publisher_client.PublisherClient.SERVICE_ADDRESS + ) == _EXPECTED_TARGET def test_init_client_options_pass_through(): @@ -182,7 +193,13 @@ def test_init_emulator(monkeypatch): # Sadly, there seems to be no good way to do this without poking at # the private API of gRPC. channel = client._transport.publish._channel - assert channel.target().decode("utf8") == "/foo/bar:123" + # Behavior to include dns prefix changed in gRPCv1.63 + grpc_major, grpc_minor = [int(part) for part in grpc.__version__.split(".")[0:2]] + if grpc_major > 1 or (grpc_major == 1 and grpc_minor >= 63): + _EXPECTED_TARGET = "dns:////foo/bar:123" + else: + _EXPECTED_TARGET = "/foo/bar:123" + assert channel.target().decode("utf8") == _EXPECTED_TARGET def test_message_ordering_enabled(creds): diff --git a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py index bedfde79a..a09d85b00 100644 --- a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py +++ b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py @@ -66,17 +66,28 @@ def test_init_w_api_endpoint(creds): client_options = {"api_endpoint": "testendpoint.google.com"} client = subscriber.Client(client_options=client_options, credentials=creds) + # Behavior to include dns prefix changed in gRPCv1.63 + grpc_major, grpc_minor = [int(part) for part in grpc.__version__.split(".")[0:2]] + if grpc_major > 1 or (grpc_major == 1 and grpc_minor >= 63): + _EXPECTED_TARGET = "dns:///testendpoint.google.com:443" + else: + _EXPECTED_TARGET = "testendpoint.google.com:443" assert (client._transport.grpc_channel._channel.target()).decode( "utf-8" - ) == "testendpoint.google.com:443" + ) == _EXPECTED_TARGET def test_init_w_empty_client_options(creds): client = subscriber.Client(client_options={}, credentials=creds) - + # Behavior to include dns prefix changed in gRPCv1.63 + grpc_major, grpc_minor = [int(part) for part in grpc.__version__.split(".")[0:2]] + if grpc_major > 1 or (grpc_major == 1 and grpc_minor >= 63): + _EXPECTED_TARGET = "dns:///pubsub.googleapis.com:443" + else: + _EXPECTED_TARGET = "pubsub.googleapis.com:443" assert (client._transport.grpc_channel._channel.target()).decode( "utf-8" - ) == subscriber_client.SubscriberClient.SERVICE_ADDRESS + ) == _EXPECTED_TARGET def test_init_client_options_pass_through(): @@ -115,7 +126,13 @@ def test_init_emulator(monkeypatch): # Sadly, there seems to be no good way to do this without poking at # the private API of gRPC. channel = client._transport.pull._channel - assert channel.target().decode("utf8") == "/baz/bacon:123" + # Behavior to include dns prefix changed in gRPCv1.63 + grpc_major, grpc_minor = [int(part) for part in grpc.__version__.split(".")[0:2]] + if grpc_major > 1 or (grpc_major == 1 and grpc_minor >= 63): + _EXPECTED_TARGET = "dns:////baz/bacon:123" + else: + _EXPECTED_TARGET = "/baz/bacon:123" + assert channel.target().decode("utf8") == _EXPECTED_TARGET def test_class_method_factory(): From b6978196df7150967c687ceb5bdc795a33047a55 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 May 2024 11:54:54 -0400 Subject: [PATCH 062/190] build(deps): bump requests from 2.31.0 to 2.32.0 in /.kokoro (#1175) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Owl Bot From ee55694d5f2e8eefc2b29ff927bb378b2df870fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 May 2024 10:58:09 -0700 Subject: [PATCH 063/190] build(deps): bump jinja2 from 3.1.3 to 3.1.4 in /.kokoro (#1172) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Owl Bot Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> From cfd7926d56cc636fca053acd18d7a03ec4e8248b Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Thu, 30 May 2024 16:36:10 -0400 Subject: [PATCH 064/190] chore(main): release 2.21.2 (#1179) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 72e6fd856..f32ff8487 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.21.1" + ".": "2.21.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0854b871b..bfd5ce7b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.21.2](https://github.com/googleapis/python-pubsub/compare/v2.21.1...v2.21.2) (2024-05-30) + + +### Bug Fixes + +* Test failures due to grpcio changes ([#1178](https://github.com/googleapis/python-pubsub/issues/1178)) ([086dd46](https://github.com/googleapis/python-pubsub/commit/086dd4660ec56d9ff2d41a32ec0b8e8dc44acc55)) + ## [2.21.1](https://github.com/googleapis/python-pubsub/compare/v2.21.0...v2.21.1) (2024-04-04) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index e71611fa6..b5c52129e 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.21.1" # {x-release-please-version} +__version__ = "2.21.2" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index e71611fa6..b5c52129e 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.21.1" # {x-release-please-version} +__version__ = "2.21.2" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 619f0eac4..5bfc0254b 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.21.1" + "version": "2.21.2" }, "snippets": [ { From e2576b6a6c81fbe610e7170bbfb45e22d7dd41db Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Fri, 7 Jun 2024 12:01:05 -0400 Subject: [PATCH 065/190] chore: Update mypy version (#1185) Co-authored-by: Owl Bot --- google/cloud/pubsub_v1/publisher/_batch/thread.py | 2 +- google/cloud/pubsub_v1/publisher/futures.py | 2 +- google/cloud/pubsub_v1/subscriber/_protocol/helper_threads.py | 4 ++-- .../pubsub_v1/subscriber/_protocol/streaming_pull_manager.py | 2 +- google/cloud/pubsub_v1/subscriber/futures.py | 2 +- google/cloud/pubsub_v1/subscriber/scheduler.py | 2 +- noxfile.py | 2 +- owlbot.py | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/google/cloud/pubsub_v1/publisher/_batch/thread.py b/google/cloud/pubsub_v1/publisher/_batch/thread.py index f6436fb7b..e6b8407fd 100644 --- a/google/cloud/pubsub_v1/publisher/_batch/thread.py +++ b/google/cloud/pubsub_v1/publisher/_batch/thread.py @@ -90,7 +90,7 @@ def __init__( client: "PublisherClient", topic: str, settings: "types.BatchSettings", - batch_done_callback: Callable[[bool], Any] = None, + batch_done_callback: Optional[Callable[[bool], Any]] = None, commit_when_full: bool = True, commit_retry: "OptionalRetry" = gapic_v1.method.DEFAULT, commit_timeout: "types.OptionalTimeout" = gapic_v1.method.DEFAULT, diff --git a/google/cloud/pubsub_v1/publisher/futures.py b/google/cloud/pubsub_v1/publisher/futures.py index c7cc66f18..7b5921673 100644 --- a/google/cloud/pubsub_v1/publisher/futures.py +++ b/google/cloud/pubsub_v1/publisher/futures.py @@ -45,7 +45,7 @@ def cancelled(self) -> bool: """ return False - def result(self, timeout: Union[int, float] = None) -> str: + def result(self, timeout: Union[int, float, None] = None) -> str: """Return the message ID or raise an exception. This blocks until the message has been published successfully and diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/helper_threads.py b/google/cloud/pubsub_v1/subscriber/_protocol/helper_threads.py index fbcab781d..a7e18a88e 100644 --- a/google/cloud/pubsub_v1/subscriber/_protocol/helper_threads.py +++ b/google/cloud/pubsub_v1/subscriber/_protocol/helper_threads.py @@ -15,7 +15,7 @@ import logging import queue import time -from typing import Any, Callable, List, Sequence +from typing import Any, Callable, List, Sequence, Optional import uuid @@ -32,7 +32,7 @@ def _get_many( - queue_: queue.Queue, max_items: int = None, max_latency: float = 0 + queue_: queue.Queue, max_items: Optional[int] = None, max_latency: float = 0 ) -> List[Any]: """Get multiple items from a Queue. diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py index f07db8546..b8531db17 100644 --- a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py +++ b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py @@ -267,7 +267,7 @@ def __init__( client: "subscriber.Client", subscription: str, flow_control: types.FlowControl = types.FlowControl(), - scheduler: ThreadScheduler = None, + scheduler: Optional[ThreadScheduler] = None, use_legacy_flow_control: bool = False, await_callbacks_on_shutdown: bool = False, ): diff --git a/google/cloud/pubsub_v1/subscriber/futures.py b/google/cloud/pubsub_v1/subscriber/futures.py index f043b7eb5..4c46c6813 100644 --- a/google/cloud/pubsub_v1/subscriber/futures.py +++ b/google/cloud/pubsub_v1/subscriber/futures.py @@ -104,7 +104,7 @@ def cancelled(self) -> bool: """ return False - def result(self, timeout: Union[int, float] = None) -> AcknowledgeStatus: + def result(self, timeout: Union[int, float, None] = None) -> AcknowledgeStatus: """Return a success code or raise an exception. This blocks until the operation completes successfully and diff --git a/google/cloud/pubsub_v1/subscriber/scheduler.py b/google/cloud/pubsub_v1/subscriber/scheduler.py index ca270a077..a3b3c88e1 100644 --- a/google/cloud/pubsub_v1/subscriber/scheduler.py +++ b/google/cloud/pubsub_v1/subscriber/scheduler.py @@ -162,7 +162,7 @@ def shutdown( work_item = self._executor._work_queue.get(block=False) if work_item is None: # Exceutor in shutdown mode. continue - dropped_messages.append(work_item.args[0]) + dropped_messages.append(work_item.args[0]) # type: ignore[index] except queue.Empty: pass diff --git a/noxfile.py b/noxfile.py index ae87830ec..61e73e0d4 100644 --- a/noxfile.py +++ b/noxfile.py @@ -32,7 +32,7 @@ ISORT_VERSION = "isort==5.11.0" LINT_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] -MYPY_VERSION = "mypy==0.910" +MYPY_VERSION = "mypy==1.10.0" DEFAULT_PYTHON_VERSION = "3.8" diff --git a/owlbot.py b/owlbot.py index d80434efd..3551413ee 100644 --- a/owlbot.py +++ b/owlbot.py @@ -348,7 +348,7 @@ s.replace( "noxfile.py", r"LINT_PATHS = \[.*?\]", - '\g<0>\n\nMYPY_VERSION = "mypy==0.910"', + '\g<0>\n\nMYPY_VERSION = "mypy==1.10.0"', ) s.replace( "noxfile.py", r'"blacken",', '\g<0>\n "mypy",', From 3698450041cb4db0e2957832c24450f674b89c11 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Fri, 7 Jun 2024 16:10:19 -0400 Subject: [PATCH 066/190] fix: Typecheck errors in samples/snippets/subscriber.py (#1186) Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- samples/snippets/mypy.ini | 6 +++++- samples/snippets/subscriber.py | 12 ++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/samples/snippets/mypy.ini b/samples/snippets/mypy.ini index 8f2bae69a..3c8dd6f41 100644 --- a/samples/snippets/mypy.ini +++ b/samples/snippets/mypy.ini @@ -4,5 +4,9 @@ strict = True exclude = noxfile\.py warn_unused_configs = True -[mypy-avro.*,backoff,flaky] +; Ignore errors caused due to missing library stubs or py.typed marker +; Refer https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-library-stubs-or-py-typed-marker +; Errors ignored instead of adding stubs as a workaround, since this directory contains sample code +; that does not affect the functionality of the client library. +[mypy-avro.*,backoff,flaky,google.cloud.*] ignore_missing_imports = True diff --git a/samples/snippets/subscriber.py b/samples/snippets/subscriber.py index ab0c8aafa..2cd03b785 100644 --- a/samples/snippets/subscriber.py +++ b/samples/snippets/subscriber.py @@ -528,8 +528,10 @@ def update_subscription_with_dead_letter_policy( ) with subscriber: - subscription_after_update = subscriber.update_subscription( - request={"subscription": subscription, "update_mask": update_mask} + subscription_after_update: gapic_types.Subscription = ( + subscriber.update_subscription( + request={"subscription": subscription, "update_mask": update_mask} + ) ) print(f"After the update: {subscription_after_update}.") @@ -573,8 +575,10 @@ def remove_dead_letter_policy( ) with subscriber: - subscription_after_update = subscriber.update_subscription( - request={"subscription": subscription, "update_mask": update_mask} + subscription_after_update: gapic_types.Subscription = ( + subscriber.update_subscription( + request={"subscription": subscription, "update_mask": update_mask} + ) ) print(f"After removing the policy: {subscription_after_update}.") From cd51149c9e0d3c59d1c75395c05308e860908bf9 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Mon, 10 Jun 2024 10:59:49 -0400 Subject: [PATCH 067/190] fix: Suppress warnings caused during pytest runs (#1189) Co-authored-by: Anthonios Partheniou --- pytest.ini | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index ce16e4730..865897dd5 100644 --- a/pytest.ini +++ b/pytest.ini @@ -13,4 +13,8 @@ filterwarnings = # Remove warning once https://github.com/googleapis/gapic-generator-python/issues/1939 is fixed ignore:get_mtls_endpoint_and_cert_source is deprecated.:DeprecationWarning # Remove warning once https://github.com/grpc/grpc/issues/35974 is fixed - ignore:unclosed:ResourceWarning \ No newline at end of file + ignore:unclosed:ResourceWarning + # Added to suppress "DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html" + # Remove once the minimum supported version of googleapis-common-protos is 1.62.0 + ignore:.*pkg_resources.declare_namespace:DeprecationWarning + ignore:.*pkg_resources is deprecated as an API:DeprecationWarning \ No newline at end of file From d12bac6d94b337aa8978006600fb00e5b13d741d Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Mon, 10 Jun 2024 12:07:09 -0400 Subject: [PATCH 068/190] fix: Race condition where future callbacks invoked before client is in paused state (#1145) Co-authored-by: Owl Bot --- google/cloud/pubsub_v1/publisher/_batch/thread.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/google/cloud/pubsub_v1/publisher/_batch/thread.py b/google/cloud/pubsub_v1/publisher/_batch/thread.py index e6b8407fd..1617f8c90 100644 --- a/google/cloud/pubsub_v1/publisher/_batch/thread.py +++ b/google/cloud/pubsub_v1/publisher/_batch/thread.py @@ -282,14 +282,14 @@ def _commit(self) -> None: # all futures and exit. self._status = base.BatchStatus.ERROR - for future in self._futures: - future.set_exception(exc) - batch_transport_succeeded = False if self._batch_done_callback is not None: # Failed to publish batch. self._batch_done_callback(batch_transport_succeeded) + for future in self._futures: + future.set_exception(exc) + return end = time.time() From 509460520d6514361cc18675040b8b86a79d0271 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Mon, 10 Jun 2024 09:51:02 -0700 Subject: [PATCH 069/190] chore(main): release 2.21.3 (#1190) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f32ff8487..f893c8067 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.21.2" + ".": "2.21.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index bfd5ce7b1..bc4d2f3d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.21.3](https://github.com/googleapis/python-pubsub/compare/v2.21.2...v2.21.3) (2024-06-10) + + +### Bug Fixes + +* Race condition where future callbacks invoked before client is in paused state ([#1145](https://github.com/googleapis/python-pubsub/issues/1145)) ([d12bac6](https://github.com/googleapis/python-pubsub/commit/d12bac6d94b337aa8978006600fb00e5b13d741d)) +* Suppress warnings caused during pytest runs ([#1189](https://github.com/googleapis/python-pubsub/issues/1189)) ([cd51149](https://github.com/googleapis/python-pubsub/commit/cd51149c9e0d3c59d1c75395c05308e860908bf9)) +* Typecheck errors in samples/snippets/subscriber.py ([#1186](https://github.com/googleapis/python-pubsub/issues/1186)) ([3698450](https://github.com/googleapis/python-pubsub/commit/3698450041cb4db0e2957832c24450f674b89c11)) + ## [2.21.2](https://github.com/googleapis/python-pubsub/compare/v2.21.1...v2.21.2) (2024-05-30) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index b5c52129e..71dc4e9b4 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.21.2" # {x-release-please-version} +__version__ = "2.21.3" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index b5c52129e..71dc4e9b4 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.21.2" # {x-release-please-version} +__version__ = "2.21.3" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 5bfc0254b..74c5d4caa 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.21.2" + "version": "2.21.3" }, "snippets": [ { From d8e8aa59ab0288fdaf5a1cc5e476581e73d0f82c Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Tue, 18 Jun 2024 10:01:24 -0400 Subject: [PATCH 070/190] docs(samples): Add code sample for optimistic subscribe (#1182) Co-authored-by: Owl Bot --- samples/snippets/subscriber.py | 93 +++++++++++++++++++++++++++++ samples/snippets/subscriber_test.py | 50 ++++++++++++++++ 2 files changed, 143 insertions(+) diff --git a/samples/snippets/subscriber.py b/samples/snippets/subscriber.py index 2cd03b785..79cd0ebf1 100644 --- a/samples/snippets/subscriber.py +++ b/samples/snippets/subscriber.py @@ -94,6 +94,86 @@ def create_subscription(project_id: str, topic_id: str, subscription_id: str) -> # [END pubsub_create_pull_subscription] +def optimistic_subscribe( + project_id: str, + topic_id: str, + subscription_id: str, + timeout: Optional[float] = None, +) -> None: + """Optimistically subscribe to messages instead of making calls to verify existence + of a subscription first and then subscribing to messages from it. This avoids admin + operation calls to verify the existence of a subscription and reduces the probability + of running out of quota for admin operations.""" + # [START pubsub_optimistic_subscribe] + from google.api_core.exceptions import NotFound + from google.cloud import pubsub_v1 + from concurrent.futures import TimeoutError + + # TODO(developer) + # project_id = "your-project-id" + # subscription_id = "your-subscription-id" + # Number of seconds the subscriber should listen for messages + # timeout = 5.0 + # topic_id = "your-topic-id" + + # Create a subscriber client. + subscriber = pubsub_v1.SubscriberClient() + + # The `subscription_path` method creates a fully qualified identifier + # in the form `projects/{project_id}/subscriptions/{subscription_id}` + subscription_path = subscriber.subscription_path(project_id, subscription_id) + + # Define callback to be called when a message is received. + def callback(message: pubsub_v1.subscriber.message.Message) -> None: + # Ack message after processing it. + message.ack() + + # Wrap subscriber in a 'with' block to automatically call close() when done. + with subscriber: + try: + # Optimistically subscribe to messages on the subscription. + streaming_pull_future = subscriber.subscribe( + subscription_path, callback=callback + ) + streaming_pull_future.result(timeout=timeout) + except TimeoutError: + print("Successfully subscribed until the timeout passed.") + streaming_pull_future.cancel() # Trigger the shutdown. + streaming_pull_future.result() # Block until the shutdown is complete. + except NotFound: + print(f"Subscription {subscription_path} not found, creating it.") + + try: + # If the subscription does not exist, then create it. + publisher = pubsub_v1.PublisherClient() + topic_path = publisher.topic_path(project_id, topic_id) + subscription = subscriber.create_subscription( + request={"name": subscription_path, "topic": topic_path} + ) + + if subscription: + print(f"Subscription {subscription.name} created") + else: + raise ValueError("Subscription creation failed.") + + # Subscribe on the created subscription. + try: + streaming_pull_future = subscriber.subscribe( + subscription.name, callback=callback + ) + streaming_pull_future.result(timeout=timeout) + except TimeoutError: + streaming_pull_future.cancel() # Trigger the shutdown. + streaming_pull_future.result() # Block until the shutdown is complete. + except Exception as e: + print( + f"Exception occurred when creating subscription and subscribing to it: {e}" + ) + except Exception as e: + print(f"Exception occurred when attempting optimistic subscribe: {e}") + # [END pubsub_optimistic_subscribe] + + def create_subscription_with_dead_letter_topic( project_id: str, topic_id: str, @@ -1161,6 +1241,15 @@ def callback(message: pubsub_v1.subscriber.message.Message) -> None: remove_dead_letter_policy_parser.add_argument("topic_id") remove_dead_letter_policy_parser.add_argument("subscription_id") + optimistic_subscribe_parser = subparsers.add_parser( + "optimistic-subscribe", help=optimistic_subscribe.__doc__ + ) + optimistic_subscribe_parser.add_argument("topic_id") + optimistic_subscribe_parser.add_argument("subscription_id") + optimistic_subscribe_parser.add_argument( + "timeout", default=None, type=float, nargs="?" + ) + receive_parser = subparsers.add_parser("receive", help=receive_messages.__doc__) receive_parser.add_argument("subscription_id") receive_parser.add_argument("timeout", default=None, type=float, nargs="?") @@ -1303,6 +1392,10 @@ def callback(message: pubsub_v1.subscriber.message.Message) -> None: ) elif args.command == "remove-dead-letter-policy": remove_dead_letter_policy(args.project_id, args.topic_id, args.subscription_id) + elif args.command == "optimistic-subscribe": + optimistic_subscribe( + args.project_id, args.topic_id, args.subscription_id, args.timeout + ) elif args.command == "receive": receive_messages(args.project_id, args.subscription_id, args.timeout) elif args.command == "receive-custom-attributes": diff --git a/samples/snippets/subscriber_test.py b/samples/snippets/subscriber_test.py index 53fefa109..86f7a94ce 100644 --- a/samples/snippets/subscriber_test.py +++ b/samples/snippets/subscriber_test.py @@ -234,6 +234,56 @@ def test_create_subscription( subscriber_client.delete_subscription(request={"subscription": subscription_path}) +def test_optimistic_subscribe( + subscriber_client: pubsub_v1.SubscriberClient, + topic: str, + publisher_client: pubsub_v1.PublisherClient, + capsys: CaptureFixture[str], +) -> None: + subscription_id = f"subscription_for_optimistic_subscribe-{PY_VERSION}-{UUID}" + subscription_path = subscriber_client.subscription_path(PROJECT_ID, subscription_id) + # Ensure there is no pre-existing subscription. + # So that we can test the case where optimistic subscribe fails. + try: + subscriber_client.delete_subscription( + request={"subscription": subscription_path} + ) + except NotFound: + pass + + # Invoke optimistic_subscribe when the subscription is not present. + # This tests scenario where optimistic subscribe fails. + subscriber.optimistic_subscribe(PROJECT_ID, TOPIC, subscription_id, 5) + out, _ = capsys.readouterr() + # Verify optimistic subscription failed. + assert f"Subscription {subscription_path} not found, creating it." in out + # Verify that subscription created due to optimistic subscribe failure. + assert f"Subscription {subscription_path} created" in out + # Verify that subscription didn't already exist. + assert "Successfully subscribed until the timeout passed." not in out + + # Invoke optimistic_subscribe when the subscription is present. + # This tests scenario where optimistic subscribe succeeds. + subscriber.optimistic_subscribe(PROJECT_ID, TOPIC, subscription_id, 5) + + out, _ = capsys.readouterr() + # Verify optimistic subscription succeeded. + assert f"Subscription {subscription_path} not found, creating it." not in out + # Verify that subscription was not created due to optimistic subscribe failure. + assert f"Subscription {subscription_path} created" not in out + # Verify that subscription already existed. + assert "Successfully subscribed until the timeout passed." in out + + # Test case where optimistic subscribe throws an exception other than NotFound + # or TimeoutError. + subscriber.optimistic_subscribe(PROJECT_ID, TOPIC, "123", 5) + out, _ = capsys.readouterr() + assert "Exception occurred when attempting optimistic subscribe:" in out + + # Clean up resources created during test. + subscriber_client.delete_subscription(request={"subscription": subscription_path}) + + def test_create_subscription_with_dead_letter_policy( subscriber_client: pubsub_v1.SubscriberClient, dead_letter_topic: str, From ae48d87b2f196debef0f5ed70cda73f0eacd47b7 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 07:45:42 -0700 Subject: [PATCH 071/190] chore(main): release 2.21.4 (#1196) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f893c8067..2dc14de52 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.21.3" + ".": "2.21.4" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index bc4d2f3d0..bbb545d50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.21.4](https://github.com/googleapis/python-pubsub/compare/v2.21.3...v2.21.4) (2024-06-18) + + +### Documentation + +* **samples:** Add code sample for optimistic subscribe ([#1182](https://github.com/googleapis/python-pubsub/issues/1182)) ([d8e8aa5](https://github.com/googleapis/python-pubsub/commit/d8e8aa59ab0288fdaf5a1cc5e476581e73d0f82c)) + ## [2.21.3](https://github.com/googleapis/python-pubsub/compare/v2.21.2...v2.21.3) (2024-06-10) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 71dc4e9b4..b51b6dad8 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.21.3" # {x-release-please-version} +__version__ = "2.21.4" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 71dc4e9b4..b51b6dad8 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.21.3" # {x-release-please-version} +__version__ = "2.21.4" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 74c5d4caa..a3e95a2fc 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.21.3" + "version": "2.21.4" }, "snippets": [ { From 0b8d7b91da7928e8f06379d19c616e7a6a411349 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 18 Jun 2024 08:48:04 -0700 Subject: [PATCH 072/190] build(deps): bump urllib3 from 2.0.7 to 2.2.2 in /.kokoro (#1195) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From a369f04c46e4b3db34dcf8cc2ef7cda4ea491e26 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Thu, 20 Jun 2024 11:14:20 -0400 Subject: [PATCH 073/190] chore: Remove restriction that protobuf should be less than 5 (#1193) Co-authored-by: Anthonios Partheniou --- pytest.ini | 4 +++- setup.py | 2 +- testing/constraints-3.7.txt | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pytest.ini b/pytest.ini index 865897dd5..34f393316 100644 --- a/pytest.ini +++ b/pytest.ini @@ -17,4 +17,6 @@ filterwarnings = # Added to suppress "DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html" # Remove once the minimum supported version of googleapis-common-protos is 1.62.0 ignore:.*pkg_resources.declare_namespace:DeprecationWarning - ignore:.*pkg_resources is deprecated as an API:DeprecationWarning \ No newline at end of file + ignore:.*pkg_resources is deprecated as an API:DeprecationWarning + # Remove once https://github.com/googleapis/python-pubsub/issues/1206 is fixed. + ignore:.*connections\(\) is deprecated and will be removed; use net_connections\(\) instead:DeprecationWarning \ No newline at end of file diff --git a/setup.py b/setup.py index a6af31207..dbb66cf7c 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ "google-api-core[grpc] >= 1.34.0, <3.0.0dev,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,!=2.10.*", "proto-plus >= 1.22.0, <2.0.0dev", "proto-plus >= 1.22.2, <2.0.0dev; python_version>='3.11'", - "protobuf>=3.19.5,<5.0.0dev,!=3.20.0,!=3.20.1,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5", + "protobuf>=3.20.2,<6.0.0dev,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5", "grpc-google-iam-v1 >= 0.12.4, < 1.0.0dev", "grpcio-status >= 1.33.2", ] diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt index ee447eb62..08db5de87 100644 --- a/testing/constraints-3.7.txt +++ b/testing/constraints-3.7.txt @@ -7,7 +7,7 @@ google-api-core==1.34.0 google-auth==2.14.1 proto-plus==1.22.0 -protobuf==3.19.5 +protobuf==3.20.2 grpc-google-iam-v1==0.12.4 grpcio==1.51.3 grpcio-status==1.33.2 From 149fa7704a746c6b0c04b3128c06680dd768a322 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Thu, 20 Jun 2024 12:59:23 -0400 Subject: [PATCH 074/190] chore(main): release 2.21.5 (#1207) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2dc14de52..cd075e262 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.21.4" + ".": "2.21.5" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index bbb545d50..96442ed5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.21.5](https://github.com/googleapis/python-pubsub/compare/v2.21.4...v2.21.5) (2024-06-20) + + +### Bug Fixes + +* Allow Protobuf 5.x ([a369f04](https://github.com/googleapis/python-pubsub/commit/a369f04c46e4b3db34dcf8cc2ef7cda4ea491e26)) + ## [2.21.4](https://github.com/googleapis/python-pubsub/compare/v2.21.3...v2.21.4) (2024-06-18) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index b51b6dad8..1f7391fd4 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.21.4" # {x-release-please-version} +__version__ = "2.21.5" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index b51b6dad8..1f7391fd4 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.21.4" # {x-release-please-version} +__version__ = "2.21.5" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index a3e95a2fc..2ab8d41b7 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.21.4" + "version": "2.21.5" }, "snippets": [ { From 27f614444a692dc6420a395065a8c90485905faf Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 12:28:05 -0700 Subject: [PATCH 075/190] chore: update templated files (#1211) Co-authored-by: Owl Bot --- .flake8 | 2 +- .github/.OwlBot.lock.yaml | 4 +- .github/auto-label.yaml | 2 +- .kokoro/build.sh | 2 +- .kokoro/docker/docs/Dockerfile | 2 +- .kokoro/populate-secrets.sh | 2 +- .kokoro/publish-docs.sh | 2 +- .kokoro/release.sh | 2 +- .kokoro/requirements.txt | 509 ++++++++++++++------------- .kokoro/test-samples-against-head.sh | 2 +- .kokoro/test-samples-impl.sh | 2 +- .kokoro/test-samples.sh | 2 +- .kokoro/trampoline.sh | 2 +- .kokoro/trampoline_v2.sh | 2 +- .pre-commit-config.yaml | 2 +- .trampolinerc | 2 +- MANIFEST.in | 2 +- docs/conf.py | 2 +- noxfile.py | 54 ++- scripts/decrypt-secrets.sh | 2 +- scripts/readme-gen/readme_gen.py | 2 +- 21 files changed, 328 insertions(+), 275 deletions(-) diff --git a/.flake8 b/.flake8 index 87f6e408c..32986c792 100644 --- a/.flake8 +++ b/.flake8 @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# 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. diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 81f87c569..91d742b5b 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:5a4c19d17e597b92d786e569be101e636c9c2817731f80a5adec56b2aa8fe070 -# created: 2024-04-12T11:35:58.922854369Z + digest: sha256:d3de8a02819f65001effcbd3ea76ce97e9bcff035c7a89457f40f892c87c5b32 +# created: 2024-07-03T17:43:00.77142528Z diff --git a/.github/auto-label.yaml b/.github/auto-label.yaml index 8b37ee897..21786a4eb 100644 --- a/.github/auto-label.yaml +++ b/.github/auto-label.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. diff --git a/.kokoro/build.sh b/.kokoro/build.sh index b3d915296..90e690e7a 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -1,5 +1,5 @@ #!/bin/bash -# 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. diff --git a/.kokoro/docker/docs/Dockerfile b/.kokoro/docker/docs/Dockerfile index bdaf39fe2..a26ce6193 100644 --- a/.kokoro/docker/docs/Dockerfile +++ b/.kokoro/docker/docs/Dockerfile @@ -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. diff --git a/.kokoro/populate-secrets.sh b/.kokoro/populate-secrets.sh index 6f3972140..c435402f4 100755 --- a/.kokoro/populate-secrets.sh +++ b/.kokoro/populate-secrets.sh @@ -1,5 +1,5 @@ #!/bin/bash -# 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. diff --git a/.kokoro/publish-docs.sh b/.kokoro/publish-docs.sh index 9eafe0be3..38f083f05 100755 --- a/.kokoro/publish-docs.sh +++ b/.kokoro/publish-docs.sh @@ -1,5 +1,5 @@ #!/bin/bash -# 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. diff --git a/.kokoro/release.sh b/.kokoro/release.sh index c333c366d..9b17b081a 100755 --- a/.kokoro/release.sh +++ b/.kokoro/release.sh @@ -1,5 +1,5 @@ #!/bin/bash -# 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. diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 51f92b8e1..35ece0e4d 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -4,21 +4,25 @@ # # pip-compile --allow-unsafe --generate-hashes requirements.in # -argcomplete==3.1.4 \ - --hash=sha256:72558ba729e4c468572609817226fb0a6e7e9a0a7d477b882be168c0b4a62b94 \ - --hash=sha256:fbe56f8cda08aa9a04b307d8482ea703e96a6a801611acb4be9bf3942017989f +argcomplete==3.4.0 \ + --hash=sha256:69a79e083a716173e5532e0fa3bef45f793f4e61096cf52b5a42c0211c8b8aa5 \ + --hash=sha256:c2abcdfe1be8ace47ba777d4fce319eb13bf8ad9dace8d085dcad6eded88057f # via nox -attrs==23.1.0 \ - --hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \ - --hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015 +attrs==23.2.0 \ + --hash=sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30 \ + --hash=sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1 # via gcp-releasetool -cachetools==5.3.2 \ - --hash=sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2 \ - --hash=sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1 +backports-tarfile==1.2.0 \ + --hash=sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34 \ + --hash=sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991 + # via jaraco-context +cachetools==5.3.3 \ + --hash=sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945 \ + --hash=sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105 # via google-auth -certifi==2023.7.22 \ - --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \ - --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9 +certifi==2024.6.2 \ + --hash=sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516 \ + --hash=sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56 # via requests cffi==1.16.0 \ --hash=sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc \ @@ -87,90 +91,90 @@ click==8.0.4 \ # -r requirements.in # gcp-docuploader # gcp-releasetool -colorlog==6.7.0 \ - --hash=sha256:0d33ca236784a1ba3ff9c532d4964126d8a2c44f1f0cb1d2b0728196f512f662 \ - --hash=sha256:bd94bd21c1e13fac7bd3153f4bc3a7dc0eb0974b8bc2fdf1a989e474f6e582e5 +colorlog==6.8.2 \ + --hash=sha256:3e3e079a41feb5a1b64f978b5ea4f46040a94f11f0e8bbb8261e3dbbeca64d44 \ + --hash=sha256:4dcbb62368e2800cb3c5abd348da7e53f6c362dda502ec27c560b2e58a66bd33 # via # gcp-docuploader # nox -cryptography==42.0.5 \ - --hash=sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee \ - --hash=sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576 \ - --hash=sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d \ - --hash=sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30 \ - --hash=sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413 \ - --hash=sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb \ - --hash=sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da \ - --hash=sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4 \ - --hash=sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd \ - --hash=sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc \ - --hash=sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8 \ - --hash=sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1 \ - --hash=sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc \ - --hash=sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e \ - --hash=sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8 \ - --hash=sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940 \ - --hash=sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400 \ - --hash=sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7 \ - --hash=sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16 \ - --hash=sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278 \ - --hash=sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74 \ - --hash=sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec \ - --hash=sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1 \ - --hash=sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2 \ - --hash=sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c \ - --hash=sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922 \ - --hash=sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a \ - --hash=sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6 \ - --hash=sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1 \ - --hash=sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e \ - --hash=sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac \ - --hash=sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7 +cryptography==42.0.8 \ + --hash=sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad \ + --hash=sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583 \ + --hash=sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b \ + --hash=sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c \ + --hash=sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1 \ + --hash=sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648 \ + --hash=sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949 \ + --hash=sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba \ + --hash=sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c \ + --hash=sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9 \ + --hash=sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d \ + --hash=sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c \ + --hash=sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e \ + --hash=sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2 \ + --hash=sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d \ + --hash=sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7 \ + --hash=sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70 \ + --hash=sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2 \ + --hash=sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7 \ + --hash=sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14 \ + --hash=sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe \ + --hash=sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e \ + --hash=sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71 \ + --hash=sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961 \ + --hash=sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7 \ + --hash=sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c \ + --hash=sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28 \ + --hash=sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842 \ + --hash=sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902 \ + --hash=sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801 \ + --hash=sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a \ + --hash=sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e # via # -r requirements.in # gcp-releasetool # secretstorage -distlib==0.3.7 \ - --hash=sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057 \ - --hash=sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8 +distlib==0.3.8 \ + --hash=sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784 \ + --hash=sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64 # via virtualenv -docutils==0.20.1 \ - --hash=sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6 \ - --hash=sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b +docutils==0.21.2 \ + --hash=sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f \ + --hash=sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2 # via readme-renderer -filelock==3.13.1 \ - --hash=sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e \ - --hash=sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c +filelock==3.15.4 \ + --hash=sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb \ + --hash=sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7 # via virtualenv gcp-docuploader==0.6.5 \ --hash=sha256:30221d4ac3e5a2b9c69aa52fdbef68cc3f27d0e6d0d90e220fc024584b8d2318 \ --hash=sha256:b7458ef93f605b9d46a4bf3a8dc1755dad1f31d030c8679edf304e343b347eea # via -r requirements.in -gcp-releasetool==2.0.0 \ - --hash=sha256:3d73480b50ba243f22d7c7ec08b115a30e1c7817c4899781840c26f9c55b8277 \ - --hash=sha256:7aa9fd935ec61e581eb8458ad00823786d91756c25e492f372b2b30962f3c28f +gcp-releasetool==2.0.1 \ + --hash=sha256:34314a910c08e8911d9c965bd44f8f2185c4f556e737d719c33a41f6a610de96 \ + --hash=sha256:b0d5863c6a070702b10883d37c4bdfd74bf930fe417f36c0c965d3b7c779ae62 # via -r requirements.in -google-api-core==2.12.0 \ - --hash=sha256:c22e01b1e3c4dcd90998494879612c38d0a3411d1f7b679eb89e2abe3ce1f553 \ - --hash=sha256:ec6054f7d64ad13b41e43d96f735acbd763b0f3b695dabaa2d579673f6a6e160 +google-api-core==2.19.1 \ + --hash=sha256:f12a9b8309b5e21d92483bbd47ce2c445861ec7d269ef6784ecc0ea8c1fa6125 \ + --hash=sha256:f4695f1e3650b316a795108a76a1c416e6afb036199d1c1f1f110916df479ffd # via # google-cloud-core # google-cloud-storage -google-auth==2.23.4 \ - --hash=sha256:79905d6b1652187def79d491d6e23d0cbb3a21d3c7ba0dbaa9c8a01906b13ff3 \ - --hash=sha256:d4bbc92fe4b8bfd2f3e8d88e5ba7085935da208ee38a134fc280e7ce682a05f2 +google-auth==2.31.0 \ + --hash=sha256:042c4702efa9f7d3c48d3a69341c209381b125faa6dbf3ebe56bc7e40ae05c23 \ + --hash=sha256:87805c36970047247c8afe614d4e3af8eceafc1ebba0c679fe75ddd1d575e871 # via # gcp-releasetool # google-api-core # google-cloud-core # google-cloud-storage -google-cloud-core==2.3.3 \ - --hash=sha256:37b80273c8d7eee1ae816b3a20ae43585ea50506cb0e60f3cf5be5f87f1373cb \ - --hash=sha256:fbd11cad3e98a7e5b0343dc07cb1039a5ffd7a5bb96e1f1e27cee4bda4a90863 +google-cloud-core==2.4.1 \ + --hash=sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073 \ + --hash=sha256:a9e6a4422b9ac5c29f79a0ede9485473338e2ce78d91f2370c01e730eab22e61 # via google-cloud-storage -google-cloud-storage==2.13.0 \ - --hash=sha256:ab0bf2e1780a1b74cf17fccb13788070b729f50c252f0c94ada2aae0ca95437d \ - --hash=sha256:f62dc4c7b6cd4360d072e3deb28035fbdad491ac3d9b0b1815a12daea10f37c7 +google-cloud-storage==2.17.0 \ + --hash=sha256:49378abff54ef656b52dca5ef0f2eba9aa83dc2b2c72c78714b03a1a95fe9388 \ + --hash=sha256:5b393bc766b7a3bc6f5407b9e665b2450d36282614b7945e570b3480a456d1e1 # via gcp-docuploader google-crc32c==1.5.0 \ --hash=sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a \ @@ -244,28 +248,36 @@ google-crc32c==1.5.0 \ # via # google-cloud-storage # google-resumable-media -google-resumable-media==2.6.0 \ - --hash=sha256:972852f6c65f933e15a4a210c2b96930763b47197cdf4aa5f5bea435efb626e7 \ - --hash=sha256:fc03d344381970f79eebb632a3c18bb1828593a2dc5572b5f90115ef7d11e81b +google-resumable-media==2.7.1 \ + --hash=sha256:103ebc4ba331ab1bfdac0250f8033627a2cd7cde09e7ccff9181e31ba4315b2c \ + --hash=sha256:eae451a7b2e2cdbaaa0fd2eb00cc8a1ee5e95e16b55597359cbc3d27d7d90e33 # via google-cloud-storage -googleapis-common-protos==1.61.0 \ - --hash=sha256:22f1915393bb3245343f6efe87f6fe868532efc12aa26b391b15132e1279f1c0 \ - --hash=sha256:8a64866a97f6304a7179873a465d6eee97b7a24ec6cfd78e0f575e96b821240b +googleapis-common-protos==1.63.2 \ + --hash=sha256:27a2499c7e8aff199665b22741997e485eccc8645aa9176c7c988e6fae507945 \ + --hash=sha256:27c5abdffc4911f28101e635de1533fb4cfd2c37fbaa9174587c799fac90aa87 # via google-api-core idna==3.7 \ --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \ --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0 # via requests -importlib-metadata==6.8.0 \ - --hash=sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb \ - --hash=sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743 +importlib-metadata==8.0.0 \ + --hash=sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f \ + --hash=sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812 # via # -r requirements.in # keyring # twine -jaraco-classes==3.3.0 \ - --hash=sha256:10afa92b6743f25c0cf5f37c6bb6e18e2c5bb84a16527ccfc0040ea377e7aaeb \ - --hash=sha256:c063dd08e89217cee02c8d5e5ec560f2c8ce6cdc2fcdc2e68f7b2e5547ed3621 +jaraco-classes==3.4.0 \ + --hash=sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd \ + --hash=sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790 + # via keyring +jaraco-context==5.3.0 \ + --hash=sha256:3e16388f7da43d384a1a7cd3452e72e14732ac9fe459678773a3608a812bf266 \ + --hash=sha256:c2f67165ce1f9be20f32f650f25d8edfc1646a8aeee48ae06fb35f90763576d2 + # via keyring +jaraco-functools==4.0.1 \ + --hash=sha256:3b24ccb921d6b593bdceb56ce14799204f473976e2a9d4b15b04d0f2c2326664 \ + --hash=sha256:d33fa765374c0611b52f8b3a795f8900869aa88c84769d4d1746cd68fb28c3e8 # via keyring jeepney==0.8.0 \ --hash=sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806 \ @@ -273,13 +285,13 @@ jeepney==0.8.0 \ # via # keyring # secretstorage -jinja2==3.1.3 \ - --hash=sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa \ - --hash=sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90 +jinja2==3.1.4 \ + --hash=sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369 \ + --hash=sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d # via gcp-releasetool -keyring==24.2.0 \ - --hash=sha256:4901caaf597bfd3bbd78c9a0c7c4c29fcd8310dab2cffefe749e916b6527acd6 \ - --hash=sha256:ca0746a19ec421219f4d713f848fa297a661a8a8c1504867e55bfb5e09091509 +keyring==25.2.1 \ + --hash=sha256:2458681cdefc0dbc0b7eb6cf75d0b98e59f9ad9b2d4edd319d18f68bdca95e50 \ + --hash=sha256:daaffd42dbda25ddafb1ad5fec4024e5bbcfe424597ca1ca452b299861e49f1b # via # gcp-releasetool # twine @@ -287,146 +299,153 @@ 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 +markupsafe==2.1.5 \ + --hash=sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf \ + --hash=sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff \ + --hash=sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f \ + --hash=sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3 \ + --hash=sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532 \ + --hash=sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f \ + --hash=sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617 \ + --hash=sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df \ + --hash=sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4 \ + --hash=sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906 \ + --hash=sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f \ + --hash=sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4 \ + --hash=sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8 \ + --hash=sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371 \ + --hash=sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2 \ + --hash=sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465 \ + --hash=sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52 \ + --hash=sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6 \ + --hash=sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169 \ + --hash=sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad \ + --hash=sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2 \ + --hash=sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0 \ + --hash=sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029 \ + --hash=sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f \ + --hash=sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a \ + --hash=sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced \ + --hash=sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5 \ + --hash=sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c \ + --hash=sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf \ + --hash=sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9 \ + --hash=sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb \ + --hash=sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad \ + --hash=sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3 \ + --hash=sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1 \ + --hash=sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46 \ + --hash=sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc \ + --hash=sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a \ + --hash=sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee \ + --hash=sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900 \ + --hash=sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5 \ + --hash=sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea \ + --hash=sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f \ + --hash=sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5 \ + --hash=sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e \ + --hash=sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a \ + --hash=sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f \ + --hash=sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50 \ + --hash=sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a \ + --hash=sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b \ + --hash=sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4 \ + --hash=sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff \ + --hash=sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2 \ + --hash=sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46 \ + --hash=sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b \ + --hash=sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf \ + --hash=sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5 \ + --hash=sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5 \ + --hash=sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab \ + --hash=sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd \ + --hash=sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68 # via jinja2 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 -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 +more-itertools==10.3.0 \ + --hash=sha256:e5d93ef411224fbcef366a6e8ddc4c5781bc6359d43412a65dd5964e46111463 \ + --hash=sha256:ea6a02e24a9161e51faad17a8782b92a0df82c12c1c8886fec7f0c3fa1a1b320 + # via + # jaraco-classes + # jaraco-functools +nh3==0.2.17 \ + --hash=sha256:0316c25b76289cf23be6b66c77d3608a4fdf537b35426280032f432f14291b9a \ + --hash=sha256:1a814dd7bba1cb0aba5bcb9bebcc88fd801b63e21e2450ae6c52d3b3336bc911 \ + --hash=sha256:1aa52a7def528297f256de0844e8dd680ee279e79583c76d6fa73a978186ddfb \ + --hash=sha256:22c26e20acbb253a5bdd33d432a326d18508a910e4dcf9a3316179860d53345a \ + --hash=sha256:40015514022af31975c0b3bca4014634fa13cb5dc4dbcbc00570acc781316dcc \ + --hash=sha256:40d0741a19c3d645e54efba71cb0d8c475b59135c1e3c580f879ad5514cbf028 \ + --hash=sha256:551672fd71d06cd828e282abdb810d1be24e1abb7ae2543a8fa36a71c1006fe9 \ + --hash=sha256:66f17d78826096291bd264f260213d2b3905e3c7fae6dfc5337d49429f1dc9f3 \ + --hash=sha256:85cdbcca8ef10733bd31f931956f7fbb85145a4d11ab9e6742bbf44d88b7e351 \ + --hash=sha256:a3f55fabe29164ba6026b5ad5c3151c314d136fd67415a17660b4aaddacf1b10 \ + --hash=sha256:b4427ef0d2dfdec10b641ed0bdaf17957eb625b2ec0ea9329b3d28806c153d71 \ + --hash=sha256:ba73a2f8d3a1b966e9cdba7b211779ad8a2561d2dba9674b8a19ed817923f65f \ + --hash=sha256:c21bac1a7245cbd88c0b0e4a420221b7bfa838a2814ee5bb924e9c2f10a1120b \ + --hash=sha256:c551eb2a3876e8ff2ac63dff1585236ed5dfec5ffd82216a7a174f7c5082a78a \ + --hash=sha256:c790769152308421283679a142dbdb3d1c46c79c823008ecea8e8141db1a2062 \ + --hash=sha256:d7a25fd8c86657f5d9d576268e3b3767c5cd4f42867c9383618be8517f0f022a # via readme-renderer -nox==2023.4.22 \ - --hash=sha256:0b1adc619c58ab4fa57d6ab2e7823fe47a32e70202f287d78474adcc7bda1891 \ - --hash=sha256:46c0560b0dc609d7d967dc99e22cb463d3c4caf54a5fda735d6c11b5177e3a9f +nox==2024.4.15 \ + --hash=sha256:6492236efa15a460ecb98e7b67562a28b70da006ab0be164e8821177577c0565 \ + --hash=sha256:ecf6700199cdfa9e5ea0a41ff5e6ef4641d09508eda6edb89d9987864115817f # via -r requirements.in -packaging==23.2 \ - --hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \ - --hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7 +packaging==24.1 \ + --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ + --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 # via # gcp-releasetool # nox -pkginfo==1.9.6 \ - --hash=sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546 \ - --hash=sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046 +pkginfo==1.10.0 \ + --hash=sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297 \ + --hash=sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097 # via twine -platformdirs==3.11.0 \ - --hash=sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3 \ - --hash=sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e +platformdirs==4.2.2 \ + --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \ + --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3 # via virtualenv -protobuf==4.25.3 \ - --hash=sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4 \ - --hash=sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8 \ - --hash=sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c \ - --hash=sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d \ - --hash=sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4 \ - --hash=sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa \ - --hash=sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c \ - --hash=sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019 \ - --hash=sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9 \ - --hash=sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c \ - --hash=sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2 +proto-plus==1.24.0 \ + --hash=sha256:30b72a5ecafe4406b0d339db35b56c4059064e69227b8c3bda7462397f966445 \ + --hash=sha256:402576830425e5f6ce4c2a6702400ac79897dab0b4343821aa5188b0fab81a12 + # via google-api-core +protobuf==5.27.2 \ + --hash=sha256:0e341109c609749d501986b835f667c6e1e24531096cff9d34ae411595e26505 \ + --hash=sha256:176c12b1f1c880bf7a76d9f7c75822b6a2bc3db2d28baa4d300e8ce4cde7409b \ + --hash=sha256:354d84fac2b0d76062e9b3221f4abbbacdfd2a4d8af36bab0474f3a0bb30ab38 \ + --hash=sha256:4fadd8d83e1992eed0248bc50a4a6361dc31bcccc84388c54c86e530b7f58863 \ + --hash=sha256:54330f07e4949d09614707c48b06d1a22f8ffb5763c159efd5c0928326a91470 \ + --hash=sha256:610e700f02469c4a997e58e328cac6f305f649826853813177e6290416e846c6 \ + --hash=sha256:7fc3add9e6003e026da5fc9e59b131b8f22b428b991ccd53e2af8071687b4fce \ + --hash=sha256:9e8f199bf7f97bd7ecebffcae45ebf9527603549b2b562df0fbc6d4d688f14ca \ + --hash=sha256:a109916aaac42bff84702fb5187f3edadbc7c97fc2c99c5ff81dd15dcce0d1e5 \ + --hash=sha256:b848dbe1d57ed7c191dfc4ea64b8b004a3f9ece4bf4d0d80a367b76df20bf36e \ + --hash=sha256:f3ecdef226b9af856075f28227ff2c90ce3a594d092c39bee5513573f25e2714 # via # gcp-docuploader # gcp-releasetool # google-api-core # googleapis-common-protos -pyasn1==0.5.0 \ - --hash=sha256:87a2121042a1ac9358cabcaf1d07680ff97ee6404333bacca15f76aa8ad01a57 \ - --hash=sha256:97b7290ca68e62a832558ec3976f15cbf911bf5d7c7039d8b861c2a0ece69fde + # proto-plus +pyasn1==0.6.0 \ + --hash=sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c \ + --hash=sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473 # via # pyasn1-modules # rsa -pyasn1-modules==0.3.0 \ - --hash=sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c \ - --hash=sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d +pyasn1-modules==0.4.0 \ + --hash=sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6 \ + --hash=sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b # via google-auth -pycparser==2.21 \ - --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \ - --hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206 +pycparser==2.22 \ + --hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \ + --hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc # via cffi -pygments==2.16.1 \ - --hash=sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692 \ - --hash=sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29 +pygments==2.18.0 \ + --hash=sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199 \ + --hash=sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a # via # readme-renderer # rich @@ -434,20 +453,20 @@ pyjwt==2.8.0 \ --hash=sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de \ --hash=sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320 # via gcp-releasetool -pyperclip==1.8.2 \ - --hash=sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57 +pyperclip==1.9.0 \ + --hash=sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310 # via gcp-releasetool -python-dateutil==2.8.2 \ - --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ - --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 +python-dateutil==2.9.0.post0 \ + --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ + --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 # via gcp-releasetool -readme-renderer==42.0 \ - --hash=sha256:13d039515c1f24de668e2c93f2e877b9dbe6c6c32328b90a40a49d8b2b85f36d \ - --hash=sha256:2d55489f83be4992fe4454939d1a051c33edbab778e82761d060c9fc6b308cd1 +readme-renderer==43.0 \ + --hash=sha256:1818dd28140813509eeed8d62687f7cd4f7bad90d4db586001c5dc09d4fde311 \ + --hash=sha256:19db308d86ecd60e5affa3b2a98f017af384678c63c88e5d4556a380e674f3f9 # via twine -requests==2.31.0 \ - --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \ - --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1 +requests==2.32.3 \ + --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \ + --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6 # via # gcp-releasetool # google-api-core @@ -462,9 +481,9 @@ rfc3986==2.0.0 \ --hash=sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd \ --hash=sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c # via twine -rich==13.6.0 \ - --hash=sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245 \ - --hash=sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef +rich==13.7.1 \ + --hash=sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222 \ + --hash=sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432 # via twine rsa==4.9 \ --hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \ @@ -480,35 +499,39 @@ six==1.16.0 \ # via # gcp-docuploader # python-dateutil -twine==4.0.2 \ - --hash=sha256:929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8 \ - --hash=sha256:9e102ef5fdd5a20661eb88fad46338806c3bd32cf1db729603fe3697b1bc83c8 +tomli==2.0.1 \ + --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ + --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f + # via nox +twine==5.1.1 \ + --hash=sha256:215dbe7b4b94c2c50a7315c0275d2258399280fbb7d04182c7e55e24b5f93997 \ + --hash=sha256:9aa0825139c02b3434d913545c7b847a21c835e11597f5255842d457da2322db # via -r requirements.in -typing-extensions==4.8.0 \ - --hash=sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0 \ - --hash=sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef +typing-extensions==4.12.2 \ + --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ + --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8 # via -r requirements.in -urllib3==2.0.7 \ - --hash=sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84 \ - --hash=sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e +urllib3==2.2.2 \ + --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \ + --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168 # via # requests # twine -virtualenv==20.24.6 \ - --hash=sha256:02ece4f56fbf939dbbc33c0715159951d6bf14aaf5457b092e4548e1382455af \ - --hash=sha256:520d056652454c5098a00c0f073611ccbea4c79089331f60bf9d7ba247bb7381 +virtualenv==20.26.3 \ + --hash=sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a \ + --hash=sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589 # via nox -wheel==0.41.3 \ - --hash=sha256:488609bc63a29322326e05560731bf7bfea8e48ad646e1f5e40d366607de0942 \ - --hash=sha256:4d4987ce51a49370ea65c0bfd2234e8ce80a12780820d9dc462597a6e60d0841 +wheel==0.43.0 \ + --hash=sha256:465ef92c69fa5c5da2d1cf8ac40559a8c940886afcef87dcf14b9470862f1d85 \ + --hash=sha256:55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81 # via -r requirements.in -zipp==3.17.0 \ - --hash=sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31 \ - --hash=sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0 +zipp==3.19.2 \ + --hash=sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19 \ + --hash=sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: -setuptools==69.2.0 \ - --hash=sha256:0ff4183f8f42cd8fa3acea16c45205521a4ef28f73c6391d8a25e92893134f2e \ - --hash=sha256:c21c49fb1042386df081cb5d86759792ab89efca84cf114889191cd09aacc80c +setuptools==70.2.0 \ + --hash=sha256:b8b8060bb426838fbe942479c90296ce976249451118ef566a5a0b7d8b78fb05 \ + --hash=sha256:bd63e505105011b25c3c11f753f7e3b8465ea739efddaccef8f0efac2137bac1 # via -r requirements.in diff --git a/.kokoro/test-samples-against-head.sh b/.kokoro/test-samples-against-head.sh index 63ac41dfa..e9d8bd79a 100755 --- a/.kokoro/test-samples-against-head.sh +++ b/.kokoro/test-samples-against-head.sh @@ -1,5 +1,5 @@ #!/bin/bash -# 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. diff --git a/.kokoro/test-samples-impl.sh b/.kokoro/test-samples-impl.sh index 5a0f5fab6..55910c8ba 100755 --- a/.kokoro/test-samples-impl.sh +++ b/.kokoro/test-samples-impl.sh @@ -1,5 +1,5 @@ #!/bin/bash -# 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. diff --git a/.kokoro/test-samples.sh b/.kokoro/test-samples.sh index 50b35a48c..7933d8201 100755 --- a/.kokoro/test-samples.sh +++ b/.kokoro/test-samples.sh @@ -1,5 +1,5 @@ #!/bin/bash -# 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. diff --git a/.kokoro/trampoline.sh b/.kokoro/trampoline.sh index d85b1f267..48f796997 100755 --- a/.kokoro/trampoline.sh +++ b/.kokoro/trampoline.sh @@ -1,5 +1,5 @@ #!/bin/bash -# 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. diff --git a/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh index 59a7cf3a9..35fa52923 100755 --- a/.kokoro/trampoline_v2.sh +++ b/.kokoro/trampoline_v2.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# 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. diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6a8e16950..1d74695f7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.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. diff --git a/.trampolinerc b/.trampolinerc index a7dfeb42c..008015237 100644 --- a/.trampolinerc +++ b/.trampolinerc @@ -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. diff --git a/MANIFEST.in b/MANIFEST.in index e0a667053..d6814cd60 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# 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. diff --git a/docs/conf.py b/docs/conf.py index bacbcd565..44d92cca7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# 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. diff --git a/noxfile.py b/noxfile.py index 61e73e0d4..8c9256d5c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# 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. @@ -214,14 +214,28 @@ def install_unittest_dependencies(session, *constraints): session.install("-e", ".", *constraints) -def default(session): +@nox.session(python=UNIT_TEST_PYTHON_VERSIONS) +@nox.parametrize( + "protobuf_implementation", + ["python", "upb", "cpp"], +) +def unit(session, protobuf_implementation): # Install all test dependencies, then install this package in-place. + if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12"): + session.skip("cpp implementation is not supported in python 3.11+") + constraints_path = str( CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" ) install_unittest_dependencies(session, "-c", constraints_path) + # TODO(https://github.com/googleapis/synthtool/issues/1976): + # Remove the 'cpp' implementation once support for Protobuf 3.x is dropped. + # The 'cpp' implementation requires Protobuf<4. + if protobuf_implementation == "cpp": + session.install("protobuf<4") + # Run py.test against the unit tests. session.run( "py.test", @@ -235,15 +249,12 @@ def default(session): "--cov-fail-under=0", os.path.join("tests", "unit"), *session.posargs, + env={ + "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation, + }, ) -@nox.session(python=UNIT_TEST_PYTHON_VERSIONS) -def unit(session): - """Run the unit test suite.""" - default(session) - - def install_systemtest_dependencies(session, *constraints): # Use pre-release gRPC for system tests. # Exclude version 1.52.0rc1 which has a known issue. @@ -412,9 +423,16 @@ def docfx(session): @nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS) -def prerelease_deps(session): +@nox.parametrize( + "protobuf_implementation", + ["python", "upb", "cpp"], +) +def prerelease_deps(session, protobuf_implementation): """Run all tests with prerelease versions of dependencies installed.""" + if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12"): + session.skip("cpp implementation is not supported in python 3.11+") + # Install all dependencies session.install("-e", ".[all, tests, tracing]") unit_deps_all = UNIT_TEST_STANDARD_DEPENDENCIES + UNIT_TEST_EXTERNAL_DEPENDENCIES @@ -449,9 +467,9 @@ def prerelease_deps(session): "protobuf", # dependency of grpc "six", + "grpc-google-iam-v1", "googleapis-common-protos", - # Exclude version 1.52.0rc1 which has a known issue. See https://github.com/grpc/grpc/issues/32163 - "grpcio!=1.52.0rc1", + "grpcio", "grpcio-status", "google-api-core", "google-auth", @@ -477,7 +495,13 @@ def prerelease_deps(session): session.run("python", "-c", "import grpc; print(grpc.__version__)") session.run("python", "-c", "import google.auth; print(google.auth.__version__)") - session.run("py.test", "tests/unit") + session.run( + "py.test", + "tests/unit", + env={ + "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation, + }, + ) system_test_path = os.path.join("tests", "system.py") system_test_folder_path = os.path.join("tests", "system") @@ -490,6 +514,9 @@ def prerelease_deps(session): f"--junitxml=system_{session.python}_sponge_log.xml", system_test_path, *session.posargs, + env={ + "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation, + }, ) if os.path.exists(system_test_folder_path): session.run( @@ -498,4 +525,7 @@ def prerelease_deps(session): f"--junitxml=system_{session.python}_sponge_log.xml", system_test_folder_path, *session.posargs, + env={ + "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation, + }, ) diff --git a/scripts/decrypt-secrets.sh b/scripts/decrypt-secrets.sh index 0018b421d..120b0ddc4 100755 --- a/scripts/decrypt-secrets.sh +++ b/scripts/decrypt-secrets.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Copyright 2023 Google LLC All rights reserved. +# Copyright 2024 Google LLC All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/scripts/readme-gen/readme_gen.py b/scripts/readme-gen/readme_gen.py index 1acc11983..8f5e248a0 100644 --- a/scripts/readme-gen/readme_gen.py +++ b/scripts/readme-gen/readme_gen.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# 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. From b2088765f6e285f4bc477105482cd63beda94a66 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 08:49:04 -0400 Subject: [PATCH 076/190] chore(python): Use latest python runtime in prerelease_deps session (#1212) Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 ++-- noxfile.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 91d742b5b..76524393f 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:d3de8a02819f65001effcbd3ea76ce97e9bcff035c7a89457f40f892c87c5b32 -# created: 2024-07-03T17:43:00.77142528Z + digest: sha256:5651442a6336971a2fb2df40fb56b3337df67cafa14c0809cc89cb34ccee1b8e +# created: 2024-07-04T19:38:10.086106449Z diff --git a/noxfile.py b/noxfile.py index 8c9256d5c..1d9b72ca7 100644 --- a/noxfile.py +++ b/noxfile.py @@ -422,7 +422,7 @@ def docfx(session): ) -@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS) +@nox.session(python="3.12") @nox.parametrize( "protobuf_implementation", ["python", "upb", "cpp"], From 191cbebdf4d712210999e975459309aecbbf2b8e Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Fri, 5 Jul 2024 14:53:22 +0200 Subject: [PATCH 077/190] chore(deps): update all dependencies (#1143) Co-authored-by: Owl Bot Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> --- samples/snippets/requirements-test.txt | 6 +++--- samples/snippets/requirements.txt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index ab0de59c1..43e018272 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1,7 +1,7 @@ backoff==2.2.1 pytest===7.4.4; python_version == '3.7' -pytest==8.1.1; python_version >= '3.8' +pytest==8.2.2; python_version >= '3.8' mock==5.1.0 flaky==3.8.1 -google-cloud-bigquery==3.20.1 -google-cloud-storage==2.16.0 +google-cloud-bigquery==3.25.0 +google-cloud-storage==2.17.0 diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 5ea08a511..9955737c3 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,5 +1,5 @@ -google-cloud-pubsub==2.21.1 +google-cloud-pubsub==2.21.5 avro==1.11.3 protobuf===4.24.4; python_version == '3.7' -protobuf==4.25.2; python_version >= '3.8' +protobuf==5.27.2; python_version >= '3.8' avro==1.11.3 From 766b3e36af30078982c45ddcc1323b1d48be960e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 20:27:07 -0700 Subject: [PATCH 078/190] build(deps): bump certifi from 2024.6.2 to 2024.7.4 in /.kokoro (#1214) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Owl Bot From 5869a740362e21702db0e64710ca18446466611e Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Sat, 6 Jul 2024 05:38:32 +0200 Subject: [PATCH 079/190] chore(deps): update all dependencies (#1213) Co-authored-by: Owl Bot From ec0cc349b344b6882979838171b6cae4209a9b02 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 21:23:36 -0700 Subject: [PATCH 080/190] feat: add use_topic_schema for Cloud Storage Subscriptions (#1154) Co-authored-by: Owl Bot Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Co-authored-by: Anthonios Partheniou --- .../services/publisher/async_client.py | 256 +-- google/pubsub_v1/services/publisher/client.py | 114 +- .../services/publisher/transports/base.py | 4 +- .../services/publisher/transports/grpc.py | 28 +- .../publisher/transports/grpc_asyncio.py | 174 +- .../services/schema_service/async_client.py | 270 +-- .../services/schema_service/client.py | 124 +- .../schema_service/transports/base.py | 4 +- .../schema_service/transports/grpc.py | 28 +- .../schema_service/transports/grpc_asyncio.py | 174 +- .../services/subscriber/async_client.py | 432 ++-- .../pubsub_v1/services/subscriber/client.py | 174 +- .../services/subscriber/transports/base.py | 4 +- .../services/subscriber/transports/grpc.py | 28 +- .../subscriber/transports/grpc_asyncio.py | 277 ++- google/pubsub_v1/types/pubsub.py | 38 + owlbot.py | 6 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- tests/unit/gapic/pubsub_v1/test_publisher.py | 1102 +++++++++- .../gapic/pubsub_v1/test_schema_service.py | 1214 ++++++++++- tests/unit/gapic/pubsub_v1/test_subscriber.py | 1924 ++++++++++++++++- .../subscriber/test_subscriber_client.py | 7 +- 22 files changed, 5424 insertions(+), 960 deletions(-) diff --git a/google/pubsub_v1/services/publisher/async_client.py b/google/pubsub_v1/services/publisher/async_client.py index 700c99c02..2b6df487d 100644 --- a/google/pubsub_v1/services/publisher/async_client.py +++ b/google/pubsub_v1/services/publisher/async_client.py @@ -18,6 +18,7 @@ import re from typing import ( Dict, + Callable, Mapping, MutableMapping, MutableSequence, @@ -37,6 +38,7 @@ from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore + try: OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault, None] except AttributeError: # pragma: NO COVER @@ -198,7 +200,9 @@ def __init__( self, *, credentials: Optional[ga_credentials.Credentials] = None, - transport: Union[str, PublisherTransport] = "grpc_asyncio", + transport: Optional[ + Union[str, PublisherTransport, Callable[..., PublisherTransport]] + ] = "grpc_asyncio", client_options: Optional[ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -210,9 +214,11 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.PublisherTransport]): The - transport to use. If set to None, a transport is chosen - automatically. + transport (Optional[Union[str,PublisherTransport,Callable[..., PublisherTransport]]]): + The transport to use, or a Callable that constructs and returns a new transport to use. + If a Callable is given, it will be called with the same set of initialization + arguments as used in the PublisherTransport constructor. + If set to None, a transport is chosen automatically. client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): Custom options for the client. @@ -323,8 +329,8 @@ async def sample_create_topic(): A topic resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( @@ -332,7 +338,10 @@ async def sample_create_topic(): "the individual field arguments should be set." ) - request = pubsub.Topic(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.Topic): + request = pubsub.Topic(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -341,20 +350,9 @@ async def sample_create_topic(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.create_topic, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.create_topic + ] # Certain fields should be provided within the metadata header; # add these here. @@ -451,8 +449,8 @@ async def sample_update_topic(): A topic resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([topic, update_mask]) if request is not None and has_flattened_params: raise ValueError( @@ -460,7 +458,10 @@ async def sample_update_topic(): "the individual field arguments should be set." ) - request = pubsub.UpdateTopicRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.UpdateTopicRequest): + request = pubsub.UpdateTopicRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -471,20 +472,9 @@ async def sample_update_topic(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.update_topic, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.update_topic + ] # Certain fields should be provided within the metadata header; # add these here. @@ -575,8 +565,8 @@ async def sample_publish(): Response for the Publish method. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([topic, messages]) if request is not None and has_flattened_params: raise ValueError( @@ -584,7 +574,10 @@ async def sample_publish(): "the individual field arguments should be set." ) - request = pubsub.PublishRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.PublishRequest): + request = pubsub.PublishRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -595,26 +588,7 @@ async def sample_publish(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.publish, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=4, - predicate=retries.if_exception_type( - core_exceptions.Aborted, - core_exceptions.Cancelled, - core_exceptions.DeadlineExceeded, - core_exceptions.InternalServerError, - core_exceptions.ResourceExhausted, - core_exceptions.ServiceUnavailable, - core_exceptions.Unknown, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[self._client._transport.publish] # Certain fields should be provided within the metadata header; # add these here. @@ -695,8 +669,8 @@ async def sample_get_topic(): A topic resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([topic]) if request is not None and has_flattened_params: raise ValueError( @@ -704,7 +678,10 @@ async def sample_get_topic(): "the individual field arguments should be set." ) - request = pubsub.GetTopicRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.GetTopicRequest): + request = pubsub.GetTopicRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -713,22 +690,9 @@ async def sample_get_topic(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.get_topic, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.Aborted, - core_exceptions.ServiceUnavailable, - core_exceptions.Unknown, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.get_topic + ] # Certain fields should be provided within the metadata header; # add these here. @@ -814,8 +778,8 @@ async def sample_list_topics(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([project]) if request is not None and has_flattened_params: raise ValueError( @@ -823,7 +787,10 @@ async def sample_list_topics(): "the individual field arguments should be set." ) - request = pubsub.ListTopicsRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.ListTopicsRequest): + request = pubsub.ListTopicsRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -832,22 +799,9 @@ async def sample_list_topics(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.list_topics, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.Aborted, - core_exceptions.ServiceUnavailable, - core_exceptions.Unknown, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.list_topics + ] # Certain fields should be provided within the metadata header; # add these here. @@ -944,8 +898,8 @@ async def sample_list_topic_subscriptions(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([topic]) if request is not None and has_flattened_params: raise ValueError( @@ -953,7 +907,10 @@ async def sample_list_topic_subscriptions(): "the individual field arguments should be set." ) - request = pubsub.ListTopicSubscriptionsRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.ListTopicSubscriptionsRequest): + request = pubsub.ListTopicSubscriptionsRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -962,22 +919,9 @@ async def sample_list_topic_subscriptions(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.list_topic_subscriptions, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.Aborted, - core_exceptions.ServiceUnavailable, - core_exceptions.Unknown, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.list_topic_subscriptions + ] # Certain fields should be provided within the metadata header; # add these here. @@ -1078,8 +1022,8 @@ async def sample_list_topic_snapshots(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([topic]) if request is not None and has_flattened_params: raise ValueError( @@ -1087,7 +1031,10 @@ async def sample_list_topic_snapshots(): "the individual field arguments should be set." ) - request = pubsub.ListTopicSnapshotsRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.ListTopicSnapshotsRequest): + request = pubsub.ListTopicSnapshotsRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -1096,22 +1043,9 @@ async def sample_list_topic_snapshots(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.list_topic_snapshots, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.Aborted, - core_exceptions.ServiceUnavailable, - core_exceptions.Unknown, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.list_topic_snapshots + ] # Certain fields should be provided within the metadata header; # add these here. @@ -1199,8 +1133,8 @@ async def sample_delete_topic(): sent along with the request as metadata. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([topic]) if request is not None and has_flattened_params: raise ValueError( @@ -1208,7 +1142,10 @@ async def sample_delete_topic(): "the individual field arguments should be set." ) - request = pubsub.DeleteTopicRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.DeleteTopicRequest): + request = pubsub.DeleteTopicRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -1217,20 +1154,9 @@ async def sample_delete_topic(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.delete_topic, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.delete_topic + ] # Certain fields should be provided within the metadata header; # add these here. @@ -1307,24 +1233,16 @@ async def sample_detach_subscription(): """ # Create or coerce a protobuf request object. - request = pubsub.DetachSubscriptionRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.DetachSubscriptionRequest): + request = pubsub.DetachSubscriptionRequest(request) # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.detach_subscription, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.detach_subscription + ] # Certain fields should be provided within the metadata header; # add these here. diff --git a/google/pubsub_v1/services/publisher/client.py b/google/pubsub_v1/services/publisher/client.py index 43ccc6df3..f07c4bbd4 100644 --- a/google/pubsub_v1/services/publisher/client.py +++ b/google/pubsub_v1/services/publisher/client.py @@ -19,6 +19,7 @@ import re from typing import ( Dict, + Callable, Mapping, MutableMapping, MutableSequence, @@ -579,7 +580,9 @@ def __init__( self, *, credentials: Optional[ga_credentials.Credentials] = None, - transport: Optional[Union[str, PublisherTransport]] = None, + transport: Optional[ + Union[str, PublisherTransport, Callable[..., PublisherTransport]] + ] = None, client_options: Optional[Union[client_options_lib.ClientOptions, dict]] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -591,9 +594,11 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, PublisherTransport]): The - transport to use. If set to None, a transport is chosen - automatically. + transport (Optional[Union[str,PublisherTransport,Callable[..., PublisherTransport]]]): + The transport to use, or a Callable that constructs and returns a new transport. + If a Callable is given, it will be called with the same set of initialization + arguments as used in the PublisherTransport constructor. + If set to None, a transport is chosen automatically. client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): Custom options for the client. @@ -699,17 +704,24 @@ def __init__( api_key_value ) - Transport = type(self).get_transport_class(cast(str, transport)) + transport_init: Union[ + Type[PublisherTransport], Callable[..., PublisherTransport] + ] = ( + type(self).get_transport_class(transport) + if isinstance(transport, str) or transport is None + else cast(Callable[..., PublisherTransport], transport) + ) + # initialize with the provided callable or the passed in class emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") if emulator_host: - if issubclass(Transport, type(self)._transport_registry["grpc"]): + if issubclass(transport_init, type(self)._transport_registry["grpc"]): channel = grpc.insecure_channel(target=emulator_host) else: channel = grpc.aio.insecure_channel(target=emulator_host) - Transport = functools.partial(Transport, channel=channel) + transport_init = functools.partial(transport_init, channel=channel) - self._transport = Transport( + self._transport = transport_init( credentials=credentials, credentials_file=self._client_options.credentials_file, host=self._api_endpoint, @@ -788,8 +800,8 @@ def sample_create_topic(): A topic resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( @@ -797,10 +809,8 @@ def sample_create_topic(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.Topic. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.Topic): request = pubsub.Topic(request) # If we have keyword arguments corresponding to fields on the @@ -907,8 +917,8 @@ def sample_update_topic(): A topic resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([topic, update_mask]) if request is not None and has_flattened_params: raise ValueError( @@ -916,10 +926,8 @@ def sample_update_topic(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.UpdateTopicRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.UpdateTopicRequest): request = pubsub.UpdateTopicRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1022,8 +1030,8 @@ def sample_publish(): Response for the Publish method. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([topic, messages]) if request is not None and has_flattened_params: raise ValueError( @@ -1031,10 +1039,8 @@ def sample_publish(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.PublishRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.PublishRequest): request = pubsub.PublishRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1127,8 +1133,8 @@ def sample_get_topic(): A topic resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([topic]) if request is not None and has_flattened_params: raise ValueError( @@ -1136,10 +1142,8 @@ def sample_get_topic(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.GetTopicRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.GetTopicRequest): request = pubsub.GetTopicRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1235,8 +1239,8 @@ def sample_list_topics(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([project]) if request is not None and has_flattened_params: raise ValueError( @@ -1244,10 +1248,8 @@ def sample_list_topics(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.ListTopicsRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.ListTopicsRequest): request = pubsub.ListTopicsRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1354,8 +1356,8 @@ def sample_list_topic_subscriptions(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([topic]) if request is not None and has_flattened_params: raise ValueError( @@ -1363,10 +1365,8 @@ def sample_list_topic_subscriptions(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.ListTopicSubscriptionsRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.ListTopicSubscriptionsRequest): request = pubsub.ListTopicSubscriptionsRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1477,8 +1477,8 @@ def sample_list_topic_snapshots(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([topic]) if request is not None and has_flattened_params: raise ValueError( @@ -1486,10 +1486,8 @@ def sample_list_topic_snapshots(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.ListTopicSnapshotsRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.ListTopicSnapshotsRequest): request = pubsub.ListTopicSnapshotsRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1587,8 +1585,8 @@ def sample_delete_topic(): sent along with the request as metadata. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([topic]) if request is not None and has_flattened_params: raise ValueError( @@ -1596,10 +1594,8 @@ def sample_delete_topic(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.DeleteTopicRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.DeleteTopicRequest): request = pubsub.DeleteTopicRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1686,10 +1682,8 @@ def sample_detach_subscription(): """ # Create or coerce a protobuf request object. - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.DetachSubscriptionRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.DetachSubscriptionRequest): request = pubsub.DetachSubscriptionRequest(request) diff --git a/google/pubsub_v1/services/publisher/transports/base.py b/google/pubsub_v1/services/publisher/transports/base.py index 47fea83e6..800ba82ce 100644 --- a/google/pubsub_v1/services/publisher/transports/base.py +++ b/google/pubsub_v1/services/publisher/transports/base.py @@ -88,6 +88,8 @@ def __init__( # Save the scopes. self._scopes = scopes + if not hasattr(self, "_ignore_credentials"): + self._ignore_credentials: bool = False # If no credentials are provided, then determine the appropriate # defaults. @@ -100,7 +102,7 @@ def __init__( credentials, _ = google.auth.load_credentials_from_file( credentials_file, **scopes_kwargs, quota_project_id=quota_project_id ) - elif credentials is None: + elif credentials is None and not self._ignore_credentials: credentials, _ = google.auth.default( **scopes_kwargs, quota_project_id=quota_project_id ) diff --git a/google/pubsub_v1/services/publisher/transports/grpc.py b/google/pubsub_v1/services/publisher/transports/grpc.py index 0fb520404..b6e07b21e 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc.py +++ b/google/pubsub_v1/services/publisher/transports/grpc.py @@ -54,7 +54,7 @@ def __init__( credentials: Optional[ga_credentials.Credentials] = None, credentials_file: Optional[str] = None, scopes: Optional[Sequence[str]] = None, - channel: Optional[grpc.Channel] = None, + channel: Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]] = None, api_mtls_endpoint: Optional[str] = None, client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]] = None, ssl_channel_credentials: Optional[grpc.ChannelCredentials] = None, @@ -74,14 +74,17 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - This argument is ignored if ``channel`` is provided. + This argument is ignored if a ``channel`` instance is provided. credentials_file (Optional[str]): A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. + This argument is ignored if a ``channel`` instance is provided. scopes (Optional(Sequence[str])): A list of scopes. This argument is - ignored if ``channel`` is provided. - channel (Optional[grpc.Channel]): A ``Channel`` instance through - which to make calls. + ignored if a ``channel`` instance is provided. + channel (Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]]): + A ``Channel`` instance through which to make calls, or a Callable + that constructs and returns one. If set to None, ``self.create_channel`` + is used to create the channel. If a Callable is given, it will be called + with the same arguments as used in ``self.create_channel``. api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. If provided, it overrides the ``host`` argument and tries to create a mutual TLS channel with client SSL credentials from @@ -91,11 +94,11 @@ def __init__( private key bytes, both in PEM format. It is ignored if ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials - for the grpc channel. It is ignored if ``channel`` is provided. + for the grpc channel. It is ignored if a ``channel`` instance is provided. client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): A callback to provide client certificate bytes and private key bytes, both in PEM format. It is used to configure a mutual TLS channel. It is - ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + ignored if a ``channel`` instance or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -121,9 +124,10 @@ def __init__( if client_cert_source: warnings.warn("client_cert_source is deprecated", DeprecationWarning) - if channel: + if isinstance(channel, grpc.Channel): # Ignore credentials if a channel was passed. - credentials = False + credentials = None + self._ignore_credentials = True # If a channel was explicitly provided, set it. self._grpc_channel = channel self._ssl_channel_credentials = None @@ -162,7 +166,9 @@ def __init__( ) if not self._grpc_channel: - self._grpc_channel = type(self).create_channel( + # initialize with the provided callable or the default channel + channel_init = channel or type(self).create_channel + self._grpc_channel = channel_init( self._host, # use the credentials which are saved credentials=self._credentials, diff --git a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py index 2d5c0137d..3d98d6b51 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py @@ -18,6 +18,8 @@ from google.api_core import gapic_v1 from google.api_core import grpc_helpers_async +from google.api_core import exceptions as core_exceptions +from google.api_core import retry_async as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore @@ -69,7 +71,6 @@ def create_channel( the credentials from the environment. credentials_file (Optional[str]): A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. scopes (Optional[Sequence[str]]): A optional list of scopes needed for this service. These are only used when credentials are not specified and are passed to :func:`google.auth.default`. @@ -99,7 +100,7 @@ def __init__( credentials: Optional[ga_credentials.Credentials] = None, credentials_file: Optional[str] = None, scopes: Optional[Sequence[str]] = None, - channel: Optional[aio.Channel] = None, + channel: Optional[Union[aio.Channel, Callable[..., aio.Channel]]] = None, api_mtls_endpoint: Optional[str] = None, client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]] = None, ssl_channel_credentials: Optional[grpc.ChannelCredentials] = None, @@ -119,15 +120,18 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - This argument is ignored if ``channel`` is provided. + This argument is ignored if a ``channel`` instance is provided. credentials_file (Optional[str]): A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. + This argument is ignored if a ``channel`` instance is provided. scopes (Optional[Sequence[str]]): A optional list of scopes needed for this service. These are only used when credentials are not specified and are passed to :func:`google.auth.default`. - channel (Optional[aio.Channel]): A ``Channel`` instance through - which to make calls. + channel (Optional[Union[aio.Channel, Callable[..., aio.Channel]]]): + A ``Channel`` instance through which to make calls, or a Callable + that constructs and returns one. If set to None, ``self.create_channel`` + is used to create the channel. If a Callable is given, it will be called + with the same arguments as used in ``self.create_channel``. api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. If provided, it overrides the ``host`` argument and tries to create a mutual TLS channel with client SSL credentials from @@ -137,11 +141,11 @@ def __init__( private key bytes, both in PEM format. It is ignored if ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials - for the grpc channel. It is ignored if ``channel`` is provided. + for the grpc channel. It is ignored if a ``channel`` instance is provided. client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): A callback to provide client certificate bytes and private key bytes, both in PEM format. It is used to configure a mutual TLS channel. It is - ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + ignored if a ``channel`` instance or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -167,9 +171,10 @@ def __init__( if client_cert_source: warnings.warn("client_cert_source is deprecated", DeprecationWarning) - if channel: + if isinstance(channel, aio.Channel): # Ignore credentials if a channel was passed. - credentials = False + credentials = None + self._ignore_credentials = True # If a channel was explicitly provided, set it. self._grpc_channel = channel self._ssl_channel_credentials = None @@ -207,7 +212,9 @@ def __init__( ) if not self._grpc_channel: - self._grpc_channel = type(self).create_channel( + # initialize with the provided callable or the default channel + channel_init = channel or type(self).create_channel + self._grpc_channel = channel_init( self._host, # use the credentials which are saved credentials=self._credentials, @@ -575,6 +582,151 @@ def test_iam_permissions( ) return self._stubs["test_iam_permissions"] + def _prep_wrapped_messages(self, client_info): + """Precompute the wrapped methods, overriding the base class method to use async wrappers.""" + self._wrapped_methods = { + self.create_topic: gapic_v1.method_async.wrap_method( + self.create_topic, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.update_topic: gapic_v1.method_async.wrap_method( + self.update_topic, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.publish: gapic_v1.method_async.wrap_method( + self.publish, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=4, + predicate=retries.if_exception_type( + core_exceptions.Aborted, + core_exceptions.Cancelled, + core_exceptions.DeadlineExceeded, + core_exceptions.InternalServerError, + core_exceptions.ResourceExhausted, + core_exceptions.ServiceUnavailable, + core_exceptions.Unknown, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.get_topic: gapic_v1.method_async.wrap_method( + self.get_topic, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.Aborted, + core_exceptions.ServiceUnavailable, + core_exceptions.Unknown, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.list_topics: gapic_v1.method_async.wrap_method( + self.list_topics, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.Aborted, + core_exceptions.ServiceUnavailable, + core_exceptions.Unknown, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.list_topic_subscriptions: gapic_v1.method_async.wrap_method( + self.list_topic_subscriptions, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.Aborted, + core_exceptions.ServiceUnavailable, + core_exceptions.Unknown, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.list_topic_snapshots: gapic_v1.method_async.wrap_method( + self.list_topic_snapshots, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.Aborted, + core_exceptions.ServiceUnavailable, + core_exceptions.Unknown, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.delete_topic: gapic_v1.method_async.wrap_method( + self.delete_topic, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.detach_subscription: gapic_v1.method_async.wrap_method( + self.detach_subscription, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + } + def close(self): return self.grpc_channel.close() diff --git a/google/pubsub_v1/services/schema_service/async_client.py b/google/pubsub_v1/services/schema_service/async_client.py index c0eb61075..f8d1ac4da 100644 --- a/google/pubsub_v1/services/schema_service/async_client.py +++ b/google/pubsub_v1/services/schema_service/async_client.py @@ -18,6 +18,7 @@ import re from typing import ( Dict, + Callable, Mapping, MutableMapping, MutableSequence, @@ -37,6 +38,7 @@ from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore + try: OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault, None] except AttributeError: # pragma: NO COVER @@ -197,7 +199,9 @@ def __init__( self, *, credentials: Optional[ga_credentials.Credentials] = None, - transport: Union[str, SchemaServiceTransport] = "grpc_asyncio", + transport: Optional[ + Union[str, SchemaServiceTransport, Callable[..., SchemaServiceTransport]] + ] = "grpc_asyncio", client_options: Optional[ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -209,9 +213,11 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.SchemaServiceTransport]): The - transport to use. If set to None, a transport is chosen - automatically. + transport (Optional[Union[str,SchemaServiceTransport,Callable[..., SchemaServiceTransport]]]): + The transport to use, or a Callable that constructs and returns a new transport to use. + If a Callable is given, it will be called with the same set of initialization + arguments as used in the SchemaServiceTransport constructor. + If set to None, a transport is chosen automatically. client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): Custom options for the client. @@ -340,8 +346,8 @@ async def sample_create_schema(): A schema resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([parent, schema, schema_id]) if request is not None and has_flattened_params: raise ValueError( @@ -349,7 +355,10 @@ async def sample_create_schema(): "the individual field arguments should be set." ) - request = gp_schema.CreateSchemaRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, gp_schema.CreateSchemaRequest): + request = gp_schema.CreateSchemaRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -362,20 +371,9 @@ async def sample_create_schema(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.create_schema, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.create_schema + ] # Certain fields should be provided within the metadata header; # add these here. @@ -455,8 +453,8 @@ async def sample_get_schema(): A schema resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( @@ -464,7 +462,10 @@ async def sample_get_schema(): "the individual field arguments should be set." ) - request = schema.GetSchemaRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, schema.GetSchemaRequest): + request = schema.GetSchemaRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -473,20 +474,9 @@ async def sample_get_schema(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.get_schema, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.get_schema + ] # Certain fields should be provided within the metadata header; # add these here. @@ -571,8 +561,8 @@ async def sample_list_schemas(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([parent]) if request is not None and has_flattened_params: raise ValueError( @@ -580,7 +570,10 @@ async def sample_list_schemas(): "the individual field arguments should be set." ) - request = schema.ListSchemasRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, schema.ListSchemasRequest): + request = schema.ListSchemasRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -589,20 +582,9 @@ async def sample_list_schemas(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.list_schemas, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.list_schemas + ] # Certain fields should be provided within the metadata header; # add these here. @@ -696,8 +678,8 @@ async def sample_list_schema_revisions(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( @@ -705,7 +687,10 @@ async def sample_list_schema_revisions(): "the individual field arguments should be set." ) - request = schema.ListSchemaRevisionsRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, schema.ListSchemaRevisionsRequest): + request = schema.ListSchemaRevisionsRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -714,20 +699,9 @@ async def sample_list_schema_revisions(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.list_schema_revisions, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.list_schema_revisions + ] # Certain fields should be provided within the metadata header; # add these here. @@ -828,8 +802,8 @@ async def sample_commit_schema(): A schema resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name, schema]) if request is not None and has_flattened_params: raise ValueError( @@ -837,7 +811,10 @@ async def sample_commit_schema(): "the individual field arguments should be set." ) - request = gp_schema.CommitSchemaRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, gp_schema.CommitSchemaRequest): + request = gp_schema.CommitSchemaRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -848,20 +825,9 @@ async def sample_commit_schema(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.commit_schema, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.commit_schema + ] # Certain fields should be provided within the metadata header; # add these here. @@ -954,8 +920,8 @@ async def sample_rollback_schema(): A schema resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name, revision_id]) if request is not None and has_flattened_params: raise ValueError( @@ -963,7 +929,10 @@ async def sample_rollback_schema(): "the individual field arguments should be set." ) - request = schema.RollbackSchemaRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, schema.RollbackSchemaRequest): + request = schema.RollbackSchemaRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -974,20 +943,9 @@ async def sample_rollback_schema(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.rollback_schema, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.rollback_schema + ] # Certain fields should be provided within the metadata header; # add these here. @@ -1078,8 +1036,8 @@ async def sample_delete_schema_revision(): A schema resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name, revision_id]) if request is not None and has_flattened_params: raise ValueError( @@ -1087,7 +1045,10 @@ async def sample_delete_schema_revision(): "the individual field arguments should be set." ) - request = schema.DeleteSchemaRevisionRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, schema.DeleteSchemaRevisionRequest): + request = schema.DeleteSchemaRevisionRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -1098,20 +1059,9 @@ async def sample_delete_schema_revision(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.delete_schema_revision, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.delete_schema_revision + ] # Certain fields should be provided within the metadata header; # add these here. @@ -1184,8 +1134,8 @@ async def sample_delete_schema(): sent along with the request as metadata. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( @@ -1193,7 +1143,10 @@ async def sample_delete_schema(): "the individual field arguments should be set." ) - request = schema.DeleteSchemaRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, schema.DeleteSchemaRequest): + request = schema.DeleteSchemaRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -1202,20 +1155,9 @@ async def sample_delete_schema(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.delete_schema, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.delete_schema + ] # Certain fields should be provided within the metadata header; # add these here. @@ -1306,8 +1248,8 @@ async def sample_validate_schema(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([parent, schema]) if request is not None and has_flattened_params: raise ValueError( @@ -1315,7 +1257,10 @@ async def sample_validate_schema(): "the individual field arguments should be set." ) - request = gp_schema.ValidateSchemaRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, gp_schema.ValidateSchemaRequest): + request = gp_schema.ValidateSchemaRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -1326,20 +1271,9 @@ async def sample_validate_schema(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.validate_schema, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.validate_schema + ] # Certain fields should be provided within the metadata header; # add these here. @@ -1414,24 +1348,16 @@ async def sample_validate_message(): """ # Create or coerce a protobuf request object. - request = schema.ValidateMessageRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, schema.ValidateMessageRequest): + request = schema.ValidateMessageRequest(request) # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.validate_message, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.validate_message + ] # Certain fields should be provided within the metadata header; # add these here. diff --git a/google/pubsub_v1/services/schema_service/client.py b/google/pubsub_v1/services/schema_service/client.py index 72fbd44c8..ca9d4f19f 100644 --- a/google/pubsub_v1/services/schema_service/client.py +++ b/google/pubsub_v1/services/schema_service/client.py @@ -19,6 +19,7 @@ import re from typing import ( Dict, + Callable, Mapping, MutableMapping, MutableSequence, @@ -528,7 +529,9 @@ def __init__( self, *, credentials: Optional[ga_credentials.Credentials] = None, - transport: Optional[Union[str, SchemaServiceTransport]] = None, + transport: Optional[ + Union[str, SchemaServiceTransport, Callable[..., SchemaServiceTransport]] + ] = None, client_options: Optional[Union[client_options_lib.ClientOptions, dict]] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -540,9 +543,11 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, SchemaServiceTransport]): The - transport to use. If set to None, a transport is chosen - automatically. + transport (Optional[Union[str,SchemaServiceTransport,Callable[..., SchemaServiceTransport]]]): + The transport to use, or a Callable that constructs and returns a new transport. + If a Callable is given, it will be called with the same set of initialization + arguments as used in the SchemaServiceTransport constructor. + If set to None, a transport is chosen automatically. client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): Custom options for the client. @@ -651,17 +656,24 @@ def __init__( api_key_value ) - Transport = type(self).get_transport_class(cast(str, transport)) + transport_init: Union[ + Type[SchemaServiceTransport], Callable[..., SchemaServiceTransport] + ] = ( + type(self).get_transport_class(transport) + if isinstance(transport, str) or transport is None + else cast(Callable[..., SchemaServiceTransport], transport) + ) + # initialize with the provided callable or the passed in class emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") if emulator_host: - if issubclass(Transport, type(self)._transport_registry["grpc"]): + if issubclass(transport_init, type(self)._transport_registry["grpc"]): channel = grpc.insecure_channel(target=emulator_host) else: channel = grpc.aio.insecure_channel(target=emulator_host) - Transport = functools.partial(Transport, channel=channel) + transport_init = functools.partial(transport_init, channel=channel) - self._transport = Transport( + self._transport = transport_init( credentials=credentials, credentials_file=self._client_options.credentials_file, host=self._api_endpoint, @@ -758,8 +770,8 @@ def sample_create_schema(): A schema resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([parent, schema, schema_id]) if request is not None and has_flattened_params: raise ValueError( @@ -767,10 +779,8 @@ def sample_create_schema(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a gp_schema.CreateSchemaRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, gp_schema.CreateSchemaRequest): request = gp_schema.CreateSchemaRequest(request) # If we have keyword arguments corresponding to fields on the @@ -864,8 +874,8 @@ def sample_get_schema(): A schema resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( @@ -873,10 +883,8 @@ def sample_get_schema(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a schema.GetSchemaRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, schema.GetSchemaRequest): request = schema.GetSchemaRequest(request) # If we have keyword arguments corresponding to fields on the @@ -971,8 +979,8 @@ def sample_list_schemas(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([parent]) if request is not None and has_flattened_params: raise ValueError( @@ -980,10 +988,8 @@ def sample_list_schemas(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a schema.ListSchemasRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, schema.ListSchemasRequest): request = schema.ListSchemasRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1087,8 +1093,8 @@ def sample_list_schema_revisions(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( @@ -1096,10 +1102,8 @@ def sample_list_schema_revisions(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a schema.ListSchemaRevisionsRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, schema.ListSchemaRevisionsRequest): request = schema.ListSchemaRevisionsRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1210,8 +1214,8 @@ def sample_commit_schema(): A schema resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name, schema]) if request is not None and has_flattened_params: raise ValueError( @@ -1219,10 +1223,8 @@ def sample_commit_schema(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a gp_schema.CommitSchemaRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, gp_schema.CommitSchemaRequest): request = gp_schema.CommitSchemaRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1327,8 +1329,8 @@ def sample_rollback_schema(): A schema resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name, revision_id]) if request is not None and has_flattened_params: raise ValueError( @@ -1336,10 +1338,8 @@ def sample_rollback_schema(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a schema.RollbackSchemaRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, schema.RollbackSchemaRequest): request = schema.RollbackSchemaRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1442,8 +1442,8 @@ def sample_delete_schema_revision(): A schema resource. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name, revision_id]) if request is not None and has_flattened_params: raise ValueError( @@ -1451,10 +1451,8 @@ def sample_delete_schema_revision(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a schema.DeleteSchemaRevisionRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, schema.DeleteSchemaRevisionRequest): request = schema.DeleteSchemaRevisionRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1539,8 +1537,8 @@ def sample_delete_schema(): sent along with the request as metadata. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name]) if request is not None and has_flattened_params: raise ValueError( @@ -1548,10 +1546,8 @@ def sample_delete_schema(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a schema.DeleteSchemaRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, schema.DeleteSchemaRequest): request = schema.DeleteSchemaRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1652,8 +1648,8 @@ def sample_validate_schema(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([parent, schema]) if request is not None and has_flattened_params: raise ValueError( @@ -1661,10 +1657,8 @@ def sample_validate_schema(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a gp_schema.ValidateSchemaRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, gp_schema.ValidateSchemaRequest): request = gp_schema.ValidateSchemaRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1751,10 +1745,8 @@ def sample_validate_message(): """ # Create or coerce a protobuf request object. - # Minor optimization to avoid making a copy if the user passes - # in a schema.ValidateMessageRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, schema.ValidateMessageRequest): request = schema.ValidateMessageRequest(request) diff --git a/google/pubsub_v1/services/schema_service/transports/base.py b/google/pubsub_v1/services/schema_service/transports/base.py index 835fcd63e..5c7f35aa8 100644 --- a/google/pubsub_v1/services/schema_service/transports/base.py +++ b/google/pubsub_v1/services/schema_service/transports/base.py @@ -89,6 +89,8 @@ def __init__( # Save the scopes. self._scopes = scopes + if not hasattr(self, "_ignore_credentials"): + self._ignore_credentials: bool = False # If no credentials are provided, then determine the appropriate # defaults. @@ -101,7 +103,7 @@ def __init__( credentials, _ = google.auth.load_credentials_from_file( credentials_file, **scopes_kwargs, quota_project_id=quota_project_id ) - elif credentials is None: + elif credentials is None and not self._ignore_credentials: credentials, _ = google.auth.default( **scopes_kwargs, quota_project_id=quota_project_id ) diff --git a/google/pubsub_v1/services/schema_service/transports/grpc.py b/google/pubsub_v1/services/schema_service/transports/grpc.py index ffe490b22..421879d19 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc.py @@ -54,7 +54,7 @@ def __init__( credentials: Optional[ga_credentials.Credentials] = None, credentials_file: Optional[str] = None, scopes: Optional[Sequence[str]] = None, - channel: Optional[grpc.Channel] = None, + channel: Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]] = None, api_mtls_endpoint: Optional[str] = None, client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]] = None, ssl_channel_credentials: Optional[grpc.ChannelCredentials] = None, @@ -74,14 +74,17 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - This argument is ignored if ``channel`` is provided. + This argument is ignored if a ``channel`` instance is provided. credentials_file (Optional[str]): A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. + This argument is ignored if a ``channel`` instance is provided. scopes (Optional(Sequence[str])): A list of scopes. This argument is - ignored if ``channel`` is provided. - channel (Optional[grpc.Channel]): A ``Channel`` instance through - which to make calls. + ignored if a ``channel`` instance is provided. + channel (Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]]): + A ``Channel`` instance through which to make calls, or a Callable + that constructs and returns one. If set to None, ``self.create_channel`` + is used to create the channel. If a Callable is given, it will be called + with the same arguments as used in ``self.create_channel``. api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. If provided, it overrides the ``host`` argument and tries to create a mutual TLS channel with client SSL credentials from @@ -91,11 +94,11 @@ def __init__( private key bytes, both in PEM format. It is ignored if ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials - for the grpc channel. It is ignored if ``channel`` is provided. + for the grpc channel. It is ignored if a ``channel`` instance is provided. client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): A callback to provide client certificate bytes and private key bytes, both in PEM format. It is used to configure a mutual TLS channel. It is - ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + ignored if a ``channel`` instance or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -121,9 +124,10 @@ def __init__( if client_cert_source: warnings.warn("client_cert_source is deprecated", DeprecationWarning) - if channel: + if isinstance(channel, grpc.Channel): # Ignore credentials if a channel was passed. - credentials = False + credentials = None + self._ignore_credentials = True # If a channel was explicitly provided, set it. self._grpc_channel = channel self._ssl_channel_credentials = None @@ -162,7 +166,9 @@ def __init__( ) if not self._grpc_channel: - self._grpc_channel = type(self).create_channel( + # initialize with the provided callable or the default channel + channel_init = channel or type(self).create_channel + self._grpc_channel = channel_init( self._host, # use the credentials which are saved credentials=self._credentials, diff --git a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py index 78c44142e..71c362436 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py @@ -18,6 +18,8 @@ from google.api_core import gapic_v1 from google.api_core import grpc_helpers_async +from google.api_core import exceptions as core_exceptions +from google.api_core import retry_async as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore @@ -69,7 +71,6 @@ def create_channel( the credentials from the environment. credentials_file (Optional[str]): A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. scopes (Optional[Sequence[str]]): A optional list of scopes needed for this service. These are only used when credentials are not specified and are passed to :func:`google.auth.default`. @@ -99,7 +100,7 @@ def __init__( credentials: Optional[ga_credentials.Credentials] = None, credentials_file: Optional[str] = None, scopes: Optional[Sequence[str]] = None, - channel: Optional[aio.Channel] = None, + channel: Optional[Union[aio.Channel, Callable[..., aio.Channel]]] = None, api_mtls_endpoint: Optional[str] = None, client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]] = None, ssl_channel_credentials: Optional[grpc.ChannelCredentials] = None, @@ -119,15 +120,18 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - This argument is ignored if ``channel`` is provided. + This argument is ignored if a ``channel`` instance is provided. credentials_file (Optional[str]): A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. + This argument is ignored if a ``channel`` instance is provided. scopes (Optional[Sequence[str]]): A optional list of scopes needed for this service. These are only used when credentials are not specified and are passed to :func:`google.auth.default`. - channel (Optional[aio.Channel]): A ``Channel`` instance through - which to make calls. + channel (Optional[Union[aio.Channel, Callable[..., aio.Channel]]]): + A ``Channel`` instance through which to make calls, or a Callable + that constructs and returns one. If set to None, ``self.create_channel`` + is used to create the channel. If a Callable is given, it will be called + with the same arguments as used in ``self.create_channel``. api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. If provided, it overrides the ``host`` argument and tries to create a mutual TLS channel with client SSL credentials from @@ -137,11 +141,11 @@ def __init__( private key bytes, both in PEM format. It is ignored if ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials - for the grpc channel. It is ignored if ``channel`` is provided. + for the grpc channel. It is ignored if a ``channel`` instance is provided. client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): A callback to provide client certificate bytes and private key bytes, both in PEM format. It is used to configure a mutual TLS channel. It is - ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + ignored if a ``channel`` instance or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -167,9 +171,10 @@ def __init__( if client_cert_source: warnings.warn("client_cert_source is deprecated", DeprecationWarning) - if channel: + if isinstance(channel, aio.Channel): # Ignore credentials if a channel was passed. - credentials = False + credentials = None + self._ignore_credentials = True # If a channel was explicitly provided, set it. self._grpc_channel = channel self._ssl_channel_credentials = None @@ -207,7 +212,9 @@ def __init__( ) if not self._grpc_channel: - self._grpc_channel = type(self).create_channel( + # initialize with the provided callable or the default channel + channel_init = channel or type(self).create_channel + self._grpc_channel = channel_init( self._host, # use the credentials which are saved credentials=self._credentials, @@ -586,6 +593,151 @@ def test_iam_permissions( ) return self._stubs["test_iam_permissions"] + def _prep_wrapped_messages(self, client_info): + """Precompute the wrapped methods, overriding the base class method to use async wrappers.""" + self._wrapped_methods = { + self.create_schema: gapic_v1.method_async.wrap_method( + self.create_schema, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.get_schema: gapic_v1.method_async.wrap_method( + self.get_schema, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.list_schemas: gapic_v1.method_async.wrap_method( + self.list_schemas, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.list_schema_revisions: gapic_v1.method_async.wrap_method( + self.list_schema_revisions, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.commit_schema: gapic_v1.method_async.wrap_method( + self.commit_schema, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.rollback_schema: gapic_v1.method_async.wrap_method( + self.rollback_schema, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.delete_schema_revision: gapic_v1.method_async.wrap_method( + self.delete_schema_revision, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.delete_schema: gapic_v1.method_async.wrap_method( + self.delete_schema, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.validate_schema: gapic_v1.method_async.wrap_method( + self.validate_schema, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.validate_message: gapic_v1.method_async.wrap_method( + self.validate_message, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + } + def close(self): return self.grpc_channel.close() diff --git a/google/pubsub_v1/services/subscriber/async_client.py b/google/pubsub_v1/services/subscriber/async_client.py index 33d8de056..1a7295131 100644 --- a/google/pubsub_v1/services/subscriber/async_client.py +++ b/google/pubsub_v1/services/subscriber/async_client.py @@ -18,6 +18,7 @@ import re from typing import ( Dict, + Callable, Mapping, MutableMapping, MutableSequence, @@ -41,6 +42,7 @@ from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore + try: OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault, None] except AttributeError: # pragma: NO COVER @@ -204,7 +206,9 @@ def __init__( self, *, credentials: Optional[ga_credentials.Credentials] = None, - transport: Union[str, SubscriberTransport] = "grpc_asyncio", + transport: Optional[ + Union[str, SubscriberTransport, Callable[..., SubscriberTransport]] + ] = "grpc_asyncio", client_options: Optional[ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -216,9 +220,11 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.SubscriberTransport]): The - transport to use. If set to None, a transport is chosen - automatically. + transport (Optional[Union[str,SubscriberTransport,Callable[..., SubscriberTransport]]]): + The transport to use, or a Callable that constructs and returns a new transport to use. + If a Callable is given, it will be called with the same set of initialization + arguments as used in the SubscriberTransport constructor. + If set to None, a transport is chosen automatically. client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): Custom options for the client. @@ -397,8 +403,8 @@ async def sample_create_subscription(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name, topic, push_config, ack_deadline_seconds]) if request is not None and has_flattened_params: raise ValueError( @@ -406,7 +412,10 @@ async def sample_create_subscription(): "the individual field arguments should be set." ) - request = pubsub.Subscription(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.Subscription): + request = pubsub.Subscription(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -421,22 +430,9 @@ async def sample_create_subscription(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.create_subscription, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.Aborted, - core_exceptions.ServiceUnavailable, - core_exceptions.Unknown, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.create_subscription + ] # Certain fields should be provided within the metadata header; # add these here. @@ -521,8 +517,8 @@ async def sample_get_subscription(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([subscription]) if request is not None and has_flattened_params: raise ValueError( @@ -530,7 +526,10 @@ async def sample_get_subscription(): "the individual field arguments should be set." ) - request = pubsub.GetSubscriptionRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.GetSubscriptionRequest): + request = pubsub.GetSubscriptionRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -539,22 +538,9 @@ async def sample_get_subscription(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.get_subscription, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.Aborted, - core_exceptions.ServiceUnavailable, - core_exceptions.Unknown, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.get_subscription + ] # Certain fields should be provided within the metadata header; # add these here. @@ -657,8 +643,8 @@ async def sample_update_subscription(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([subscription, update_mask]) if request is not None and has_flattened_params: raise ValueError( @@ -666,7 +652,10 @@ async def sample_update_subscription(): "the individual field arguments should be set." ) - request = pubsub.UpdateSubscriptionRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.UpdateSubscriptionRequest): + request = pubsub.UpdateSubscriptionRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -677,20 +666,9 @@ async def sample_update_subscription(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.update_subscription, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.update_subscription + ] # Certain fields should be provided within the metadata header; # add these here. @@ -777,8 +755,8 @@ async def sample_list_subscriptions(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([project]) if request is not None and has_flattened_params: raise ValueError( @@ -786,7 +764,10 @@ async def sample_list_subscriptions(): "the individual field arguments should be set." ) - request = pubsub.ListSubscriptionsRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.ListSubscriptionsRequest): + request = pubsub.ListSubscriptionsRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -795,22 +776,9 @@ async def sample_list_subscriptions(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.list_subscriptions, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.Aborted, - core_exceptions.ServiceUnavailable, - core_exceptions.Unknown, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.list_subscriptions + ] # Certain fields should be provided within the metadata header; # add these here. @@ -898,8 +866,8 @@ async def sample_delete_subscription(): sent along with the request as metadata. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([subscription]) if request is not None and has_flattened_params: raise ValueError( @@ -907,7 +875,10 @@ async def sample_delete_subscription(): "the individual field arguments should be set." ) - request = pubsub.DeleteSubscriptionRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.DeleteSubscriptionRequest): + request = pubsub.DeleteSubscriptionRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -916,20 +887,9 @@ async def sample_delete_subscription(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.delete_subscription, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.delete_subscription + ] # Certain fields should be provided within the metadata header; # add these here. @@ -1032,8 +992,8 @@ async def sample_modify_ack_deadline(): sent along with the request as metadata. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([subscription, ack_ids, ack_deadline_seconds]) if request is not None and has_flattened_params: raise ValueError( @@ -1041,7 +1001,10 @@ async def sample_modify_ack_deadline(): "the individual field arguments should be set." ) - request = pubsub.ModifyAckDeadlineRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.ModifyAckDeadlineRequest): + request = pubsub.ModifyAckDeadlineRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -1054,20 +1017,9 @@ async def sample_modify_ack_deadline(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.modify_ack_deadline, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.modify_ack_deadline + ] # Certain fields should be provided within the metadata header; # add these here. @@ -1157,8 +1109,8 @@ async def sample_acknowledge(): sent along with the request as metadata. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([subscription, ack_ids]) if request is not None and has_flattened_params: raise ValueError( @@ -1166,7 +1118,10 @@ async def sample_acknowledge(): "the individual field arguments should be set." ) - request = pubsub.AcknowledgeRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.AcknowledgeRequest): + request = pubsub.AcknowledgeRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -1177,20 +1132,9 @@ async def sample_acknowledge(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.acknowledge, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.acknowledge + ] # Certain fields should be provided within the metadata header; # add these here. @@ -1297,8 +1241,8 @@ async def sample_pull(): Response for the Pull method. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([subscription, return_immediately, max_messages]) if request is not None and has_flattened_params: raise ValueError( @@ -1306,7 +1250,10 @@ async def sample_pull(): "the individual field arguments should be set." ) - request = pubsub.PullRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.PullRequest): + request = pubsub.PullRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -1325,23 +1272,7 @@ async def sample_pull(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.pull, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.Aborted, - core_exceptions.InternalServerError, - core_exceptions.ServiceUnavailable, - core_exceptions.Unknown, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[self._client._transport.pull] # Certain fields should be provided within the metadata header; # add these here. @@ -1441,24 +1372,9 @@ def request_generator(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.streaming_pull, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=4, - predicate=retries.if_exception_type( - core_exceptions.Aborted, - core_exceptions.DeadlineExceeded, - core_exceptions.InternalServerError, - core_exceptions.ResourceExhausted, - core_exceptions.ServiceUnavailable, - ), - deadline=900.0, - ), - default_timeout=900.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.streaming_pull + ] # Validate the universe domain. self._client._validate_universe_domain() @@ -1545,8 +1461,8 @@ async def sample_modify_push_config(): sent along with the request as metadata. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([subscription, push_config]) if request is not None and has_flattened_params: raise ValueError( @@ -1554,7 +1470,10 @@ async def sample_modify_push_config(): "the individual field arguments should be set." ) - request = pubsub.ModifyPushConfigRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.ModifyPushConfigRequest): + request = pubsub.ModifyPushConfigRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -1565,20 +1484,9 @@ async def sample_modify_push_config(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.modify_push_config, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.modify_push_config + ] # Certain fields should be provided within the metadata header; # add these here. @@ -1668,8 +1576,8 @@ async def sample_get_snapshot(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([snapshot]) if request is not None and has_flattened_params: raise ValueError( @@ -1677,7 +1585,10 @@ async def sample_get_snapshot(): "the individual field arguments should be set." ) - request = pubsub.GetSnapshotRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.GetSnapshotRequest): + request = pubsub.GetSnapshotRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -1686,22 +1597,9 @@ async def sample_get_snapshot(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.get_snapshot, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.Aborted, - core_exceptions.ServiceUnavailable, - core_exceptions.Unknown, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.get_snapshot + ] # Certain fields should be provided within the metadata header; # add these here. @@ -1790,8 +1688,8 @@ async def sample_list_snapshots(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([project]) if request is not None and has_flattened_params: raise ValueError( @@ -1799,7 +1697,10 @@ async def sample_list_snapshots(): "the individual field arguments should be set." ) - request = pubsub.ListSnapshotsRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.ListSnapshotsRequest): + request = pubsub.ListSnapshotsRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -1808,22 +1709,9 @@ async def sample_list_snapshots(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.list_snapshots, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.Aborted, - core_exceptions.ServiceUnavailable, - core_exceptions.Unknown, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.list_snapshots + ] # Certain fields should be provided within the metadata header; # add these here. @@ -1959,8 +1847,8 @@ async def sample_create_snapshot(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name, subscription]) if request is not None and has_flattened_params: raise ValueError( @@ -1968,7 +1856,10 @@ async def sample_create_snapshot(): "the individual field arguments should be set." ) - request = pubsub.CreateSnapshotRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.CreateSnapshotRequest): + request = pubsub.CreateSnapshotRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -1979,20 +1870,9 @@ async def sample_create_snapshot(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.create_snapshot, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.create_snapshot + ] # Certain fields should be provided within the metadata header; # add these here. @@ -2092,8 +1972,8 @@ async def sample_update_snapshot(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([snapshot, update_mask]) if request is not None and has_flattened_params: raise ValueError( @@ -2101,7 +1981,10 @@ async def sample_update_snapshot(): "the individual field arguments should be set." ) - request = pubsub.UpdateSnapshotRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.UpdateSnapshotRequest): + request = pubsub.UpdateSnapshotRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -2112,20 +1995,9 @@ async def sample_update_snapshot(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.update_snapshot, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.update_snapshot + ] # Certain fields should be provided within the metadata header; # add these here. @@ -2209,8 +2081,8 @@ async def sample_delete_snapshot(): sent along with the request as metadata. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([snapshot]) if request is not None and has_flattened_params: raise ValueError( @@ -2218,7 +2090,10 @@ async def sample_delete_snapshot(): "the individual field arguments should be set." ) - request = pubsub.DeleteSnapshotRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.DeleteSnapshotRequest): + request = pubsub.DeleteSnapshotRequest(request) # If we have keyword arguments corresponding to fields on the # request, apply these. @@ -2227,20 +2102,9 @@ async def sample_delete_snapshot(): # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.delete_snapshot, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.ServiceUnavailable, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[ + self._client._transport.delete_snapshot + ] # Certain fields should be provided within the metadata header; # add these here. @@ -2317,26 +2181,14 @@ async def sample_seek(): Response for the Seek method (this response is empty). """ # Create or coerce a protobuf request object. - request = pubsub.SeekRequest(request) + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance(request, pubsub.SeekRequest): + request = pubsub.SeekRequest(request) # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.seek, - default_retry=retries.AsyncRetry( - initial=0.1, - maximum=60.0, - multiplier=1.3, - predicate=retries.if_exception_type( - core_exceptions.Aborted, - core_exceptions.ServiceUnavailable, - core_exceptions.Unknown, - ), - deadline=60.0, - ), - default_timeout=60.0, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._client._transport._wrapped_methods[self._client._transport.seek] # Certain fields should be provided within the metadata header; # add these here. diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index 6b7103852..bc7639c59 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -19,6 +19,7 @@ import re from typing import ( Dict, + Callable, Mapping, MutableMapping, MutableSequence, @@ -583,7 +584,9 @@ def __init__( self, *, credentials: Optional[ga_credentials.Credentials] = None, - transport: Optional[Union[str, SubscriberTransport]] = None, + transport: Optional[ + Union[str, SubscriberTransport, Callable[..., SubscriberTransport]] + ] = None, client_options: Optional[Union[client_options_lib.ClientOptions, dict]] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -595,9 +598,11 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, SubscriberTransport]): The - transport to use. If set to None, a transport is chosen - automatically. + transport (Optional[Union[str,SubscriberTransport,Callable[..., SubscriberTransport]]]): + The transport to use, or a Callable that constructs and returns a new transport. + If a Callable is given, it will be called with the same set of initialization + arguments as used in the SubscriberTransport constructor. + If set to None, a transport is chosen automatically. client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]): Custom options for the client. @@ -703,17 +708,24 @@ def __init__( api_key_value ) - Transport = type(self).get_transport_class(cast(str, transport)) + transport_init: Union[ + Type[SubscriberTransport], Callable[..., SubscriberTransport] + ] = ( + type(self).get_transport_class(transport) + if isinstance(transport, str) or transport is None + else cast(Callable[..., SubscriberTransport], transport) + ) + # initialize with the provided callable or the passed in class emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") if emulator_host: - if issubclass(Transport, type(self)._transport_registry["grpc"]): + if issubclass(transport_init, type(self)._transport_registry["grpc"]): channel = grpc.insecure_channel(target=emulator_host) else: channel = grpc.aio.insecure_channel(target=emulator_host) - Transport = functools.partial(Transport, channel=channel) + transport_init = functools.partial(transport_init, channel=channel) - self._transport = Transport( + self._transport = transport_init( credentials=credentials, credentials_file=self._client_options.credentials_file, host=self._api_endpoint, @@ -860,8 +872,8 @@ def sample_create_subscription(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name, topic, push_config, ack_deadline_seconds]) if request is not None and has_flattened_params: raise ValueError( @@ -869,10 +881,8 @@ def sample_create_subscription(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.Subscription. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.Subscription): request = pubsub.Subscription(request) # If we have keyword arguments corresponding to fields on the @@ -973,8 +983,8 @@ def sample_get_subscription(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([subscription]) if request is not None and has_flattened_params: raise ValueError( @@ -982,10 +992,8 @@ def sample_get_subscription(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.GetSubscriptionRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.GetSubscriptionRequest): request = pubsub.GetSubscriptionRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1098,8 +1106,8 @@ def sample_update_subscription(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([subscription, update_mask]) if request is not None and has_flattened_params: raise ValueError( @@ -1107,10 +1115,8 @@ def sample_update_subscription(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.UpdateSubscriptionRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.UpdateSubscriptionRequest): request = pubsub.UpdateSubscriptionRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1209,8 +1215,8 @@ def sample_list_subscriptions(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([project]) if request is not None and has_flattened_params: raise ValueError( @@ -1218,10 +1224,8 @@ def sample_list_subscriptions(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.ListSubscriptionsRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.ListSubscriptionsRequest): request = pubsub.ListSubscriptionsRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1319,8 +1323,8 @@ def sample_delete_subscription(): sent along with the request as metadata. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([subscription]) if request is not None and has_flattened_params: raise ValueError( @@ -1328,10 +1332,8 @@ def sample_delete_subscription(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.DeleteSubscriptionRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.DeleteSubscriptionRequest): request = pubsub.DeleteSubscriptionRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1444,8 +1446,8 @@ def sample_modify_ack_deadline(): sent along with the request as metadata. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([subscription, ack_ids, ack_deadline_seconds]) if request is not None and has_flattened_params: raise ValueError( @@ -1453,10 +1455,8 @@ def sample_modify_ack_deadline(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.ModifyAckDeadlineRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.ModifyAckDeadlineRequest): request = pubsub.ModifyAckDeadlineRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1560,8 +1560,8 @@ def sample_acknowledge(): sent along with the request as metadata. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([subscription, ack_ids]) if request is not None and has_flattened_params: raise ValueError( @@ -1569,10 +1569,8 @@ def sample_acknowledge(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.AcknowledgeRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.AcknowledgeRequest): request = pubsub.AcknowledgeRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1691,8 +1689,8 @@ def sample_pull(): Response for the Pull method. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([subscription, return_immediately, max_messages]) if request is not None and has_flattened_params: raise ValueError( @@ -1700,10 +1698,8 @@ def sample_pull(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.PullRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.PullRequest): request = pubsub.PullRequest(request) # If we have keyword arguments corresponding to fields on the @@ -1915,8 +1911,8 @@ def sample_modify_push_config(): sent along with the request as metadata. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([subscription, push_config]) if request is not None and has_flattened_params: raise ValueError( @@ -1924,10 +1920,8 @@ def sample_modify_push_config(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.ModifyPushConfigRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.ModifyPushConfigRequest): request = pubsub.ModifyPushConfigRequest(request) # If we have keyword arguments corresponding to fields on the @@ -2029,8 +2023,8 @@ def sample_get_snapshot(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([snapshot]) if request is not None and has_flattened_params: raise ValueError( @@ -2038,10 +2032,8 @@ def sample_get_snapshot(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.GetSnapshotRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.GetSnapshotRequest): request = pubsub.GetSnapshotRequest(request) # If we have keyword arguments corresponding to fields on the @@ -2140,8 +2132,8 @@ def sample_list_snapshots(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([project]) if request is not None and has_flattened_params: raise ValueError( @@ -2149,10 +2141,8 @@ def sample_list_snapshots(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.ListSnapshotsRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.ListSnapshotsRequest): request = pubsub.ListSnapshotsRequest(request) # If we have keyword arguments corresponding to fields on the @@ -2298,8 +2288,8 @@ def sample_create_snapshot(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([name, subscription]) if request is not None and has_flattened_params: raise ValueError( @@ -2307,10 +2297,8 @@ def sample_create_snapshot(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.CreateSnapshotRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.CreateSnapshotRequest): request = pubsub.CreateSnapshotRequest(request) # If we have keyword arguments corresponding to fields on the @@ -2422,8 +2410,8 @@ def sample_update_snapshot(): """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([snapshot, update_mask]) if request is not None and has_flattened_params: raise ValueError( @@ -2431,10 +2419,8 @@ def sample_update_snapshot(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.UpdateSnapshotRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.UpdateSnapshotRequest): request = pubsub.UpdateSnapshotRequest(request) # If we have keyword arguments corresponding to fields on the @@ -2530,8 +2516,8 @@ def sample_delete_snapshot(): sent along with the request as metadata. """ # Create or coerce a protobuf request object. - # Quick check: If we got a request object, we should *not* have - # gotten any keyword arguments that map to the request. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. has_flattened_params = any([snapshot]) if request is not None and has_flattened_params: raise ValueError( @@ -2539,10 +2525,8 @@ def sample_delete_snapshot(): "the individual field arguments should be set." ) - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.DeleteSnapshotRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.DeleteSnapshotRequest): request = pubsub.DeleteSnapshotRequest(request) # If we have keyword arguments corresponding to fields on the @@ -2629,10 +2613,8 @@ def sample_seek(): Response for the Seek method (this response is empty). """ # Create or coerce a protobuf request object. - # Minor optimization to avoid making a copy if the user passes - # in a pubsub.SeekRequest. - # There's no risk of modifying the input as we've already verified - # there are no flattened fields. + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. if not isinstance(request, pubsub.SeekRequest): request = pubsub.SeekRequest(request) diff --git a/google/pubsub_v1/services/subscriber/transports/base.py b/google/pubsub_v1/services/subscriber/transports/base.py index 34cb97d49..c5fa183b1 100644 --- a/google/pubsub_v1/services/subscriber/transports/base.py +++ b/google/pubsub_v1/services/subscriber/transports/base.py @@ -88,6 +88,8 @@ def __init__( # Save the scopes. self._scopes = scopes + if not hasattr(self, "_ignore_credentials"): + self._ignore_credentials: bool = False # If no credentials are provided, then determine the appropriate # defaults. @@ -100,7 +102,7 @@ def __init__( credentials, _ = google.auth.load_credentials_from_file( credentials_file, **scopes_kwargs, quota_project_id=quota_project_id ) - elif credentials is None: + elif credentials is None and not self._ignore_credentials: credentials, _ = google.auth.default( **scopes_kwargs, quota_project_id=quota_project_id ) diff --git a/google/pubsub_v1/services/subscriber/transports/grpc.py b/google/pubsub_v1/services/subscriber/transports/grpc.py index 82c752fa5..e706190ce 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc.py @@ -56,7 +56,7 @@ def __init__( credentials: Optional[ga_credentials.Credentials] = None, credentials_file: Optional[str] = None, scopes: Optional[Sequence[str]] = None, - channel: Optional[grpc.Channel] = None, + channel: Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]] = None, api_mtls_endpoint: Optional[str] = None, client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]] = None, ssl_channel_credentials: Optional[grpc.ChannelCredentials] = None, @@ -76,14 +76,17 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - This argument is ignored if ``channel`` is provided. + This argument is ignored if a ``channel`` instance is provided. credentials_file (Optional[str]): A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. + This argument is ignored if a ``channel`` instance is provided. scopes (Optional(Sequence[str])): A list of scopes. This argument is - ignored if ``channel`` is provided. - channel (Optional[grpc.Channel]): A ``Channel`` instance through - which to make calls. + ignored if a ``channel`` instance is provided. + channel (Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]]): + A ``Channel`` instance through which to make calls, or a Callable + that constructs and returns one. If set to None, ``self.create_channel`` + is used to create the channel. If a Callable is given, it will be called + with the same arguments as used in ``self.create_channel``. api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. If provided, it overrides the ``host`` argument and tries to create a mutual TLS channel with client SSL credentials from @@ -93,11 +96,11 @@ def __init__( private key bytes, both in PEM format. It is ignored if ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials - for the grpc channel. It is ignored if ``channel`` is provided. + for the grpc channel. It is ignored if a ``channel`` instance is provided. client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): A callback to provide client certificate bytes and private key bytes, both in PEM format. It is used to configure a mutual TLS channel. It is - ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + ignored if a ``channel`` instance or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -123,9 +126,10 @@ def __init__( if client_cert_source: warnings.warn("client_cert_source is deprecated", DeprecationWarning) - if channel: + if isinstance(channel, grpc.Channel): # Ignore credentials if a channel was passed. - credentials = False + credentials = None + self._ignore_credentials = True # If a channel was explicitly provided, set it. self._grpc_channel = channel self._ssl_channel_credentials = None @@ -164,7 +168,9 @@ def __init__( ) if not self._grpc_channel: - self._grpc_channel = type(self).create_channel( + # initialize with the provided callable or the default channel + channel_init = channel or type(self).create_channel + self._grpc_channel = channel_init( self._host, # use the credentials which are saved credentials=self._credentials, diff --git a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py index 356acf17c..9dab4a21b 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py @@ -18,6 +18,8 @@ from google.api_core import gapic_v1 from google.api_core import grpc_helpers_async +from google.api_core import exceptions as core_exceptions +from google.api_core import retry_async as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore @@ -71,7 +73,6 @@ def create_channel( the credentials from the environment. credentials_file (Optional[str]): A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. scopes (Optional[Sequence[str]]): A optional list of scopes needed for this service. These are only used when credentials are not specified and are passed to :func:`google.auth.default`. @@ -101,7 +102,7 @@ def __init__( credentials: Optional[ga_credentials.Credentials] = None, credentials_file: Optional[str] = None, scopes: Optional[Sequence[str]] = None, - channel: Optional[aio.Channel] = None, + channel: Optional[Union[aio.Channel, Callable[..., aio.Channel]]] = None, api_mtls_endpoint: Optional[str] = None, client_cert_source: Optional[Callable[[], Tuple[bytes, bytes]]] = None, ssl_channel_credentials: Optional[grpc.ChannelCredentials] = None, @@ -121,15 +122,18 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - This argument is ignored if ``channel`` is provided. + This argument is ignored if a ``channel`` instance is provided. credentials_file (Optional[str]): A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. + This argument is ignored if a ``channel`` instance is provided. scopes (Optional[Sequence[str]]): A optional list of scopes needed for this service. These are only used when credentials are not specified and are passed to :func:`google.auth.default`. - channel (Optional[aio.Channel]): A ``Channel`` instance through - which to make calls. + channel (Optional[Union[aio.Channel, Callable[..., aio.Channel]]]): + A ``Channel`` instance through which to make calls, or a Callable + that constructs and returns one. If set to None, ``self.create_channel`` + is used to create the channel. If a Callable is given, it will be called + with the same arguments as used in ``self.create_channel``. api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. If provided, it overrides the ``host`` argument and tries to create a mutual TLS channel with client SSL credentials from @@ -139,11 +143,11 @@ def __init__( private key bytes, both in PEM format. It is ignored if ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials - for the grpc channel. It is ignored if ``channel`` is provided. + for the grpc channel. It is ignored if a ``channel`` instance is provided. client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): A callback to provide client certificate bytes and private key bytes, both in PEM format. It is used to configure a mutual TLS channel. It is - ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + ignored if a ``channel`` instance or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -169,9 +173,10 @@ def __init__( if client_cert_source: warnings.warn("client_cert_source is deprecated", DeprecationWarning) - if channel: + if isinstance(channel, aio.Channel): # Ignore credentials if a channel was passed. - credentials = False + credentials = None + self._ignore_credentials = True # If a channel was explicitly provided, set it. self._grpc_channel = channel self._ssl_channel_credentials = None @@ -209,7 +214,9 @@ def __init__( ) if not self._grpc_channel: - self._grpc_channel = type(self).create_channel( + # initialize with the provided callable or the default channel + channel_init = channel or type(self).create_channel + self._grpc_channel = channel_init( self._host, # use the credentials which are saved credentials=self._credentials, @@ -832,6 +839,254 @@ def test_iam_permissions( ) return self._stubs["test_iam_permissions"] + def _prep_wrapped_messages(self, client_info): + """Precompute the wrapped methods, overriding the base class method to use async wrappers.""" + self._wrapped_methods = { + self.create_subscription: gapic_v1.method_async.wrap_method( + self.create_subscription, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.Aborted, + core_exceptions.ServiceUnavailable, + core_exceptions.Unknown, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.get_subscription: gapic_v1.method_async.wrap_method( + self.get_subscription, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.Aborted, + core_exceptions.ServiceUnavailable, + core_exceptions.Unknown, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.update_subscription: gapic_v1.method_async.wrap_method( + self.update_subscription, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.list_subscriptions: gapic_v1.method_async.wrap_method( + self.list_subscriptions, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.Aborted, + core_exceptions.ServiceUnavailable, + core_exceptions.Unknown, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.delete_subscription: gapic_v1.method_async.wrap_method( + self.delete_subscription, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.modify_ack_deadline: gapic_v1.method_async.wrap_method( + self.modify_ack_deadline, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.acknowledge: gapic_v1.method_async.wrap_method( + self.acknowledge, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.pull: gapic_v1.method_async.wrap_method( + self.pull, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.Aborted, + core_exceptions.InternalServerError, + core_exceptions.ServiceUnavailable, + core_exceptions.Unknown, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.streaming_pull: gapic_v1.method_async.wrap_method( + self.streaming_pull, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=4, + predicate=retries.if_exception_type( + core_exceptions.Aborted, + core_exceptions.DeadlineExceeded, + core_exceptions.InternalServerError, + core_exceptions.ResourceExhausted, + core_exceptions.ServiceUnavailable, + ), + deadline=900.0, + ), + default_timeout=900.0, + client_info=client_info, + ), + self.modify_push_config: gapic_v1.method_async.wrap_method( + self.modify_push_config, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.get_snapshot: gapic_v1.method_async.wrap_method( + self.get_snapshot, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.Aborted, + core_exceptions.ServiceUnavailable, + core_exceptions.Unknown, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.list_snapshots: gapic_v1.method_async.wrap_method( + self.list_snapshots, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.Aborted, + core_exceptions.ServiceUnavailable, + core_exceptions.Unknown, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.create_snapshot: gapic_v1.method_async.wrap_method( + self.create_snapshot, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.update_snapshot: gapic_v1.method_async.wrap_method( + self.update_snapshot, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.delete_snapshot: gapic_v1.method_async.wrap_method( + self.delete_snapshot, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.ServiceUnavailable, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + self.seek: gapic_v1.method_async.wrap_method( + self.seek, + default_retry=retries.AsyncRetry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type( + core_exceptions.Aborted, + core_exceptions.ServiceUnavailable, + core_exceptions.Unknown, + ), + deadline=60.0, + ), + default_timeout=60.0, + client_info=client_info, + ), + } + def close(self): return self.grpc_channel.close() diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index 55cda3abd..a45a34a14 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -1302,6 +1302,14 @@ class BigQueryConfig(proto.Message): Optional. When true, use the BigQuery table's schema as the columns to write to in BigQuery. ``use_table_schema`` and ``use_topic_schema`` cannot be enabled at the same time. + service_account_email (str): + Optional. The service account to use to write to BigQuery. + The subscription creator or updater that specifies this + field must have ``iam.serviceAccounts.actAs`` permission on + the service account. If not specified, the Pub/Sub `service + agent `__, + service-{project_number}@gcp-sa-pubsub.iam.gserviceaccount.com, + is used. """ class State(proto.Enum): @@ -1366,6 +1374,10 @@ class State(proto.Enum): proto.BOOL, number=6, ) + service_account_email: str = proto.Field( + proto.STRING, + number=7, + ) class CloudStorageConfig(proto.Message): @@ -1424,6 +1436,15 @@ class CloudStorageConfig(proto.Message): Output only. An output-only field that indicates whether or not the subscription can receive messages. + service_account_email (str): + Optional. The service account to use to write to Cloud + Storage. The subscription creator or updater that specifies + this field must have ``iam.serviceAccounts.actAs`` + permission on the service account. If not specified, the + Pub/Sub `service + agent `__, + service-{project_number}@gcp-sa-pubsub.iam.gserviceaccount.com, + is used. """ class State(proto.Enum): @@ -1445,12 +1466,17 @@ class State(proto.Enum): Cannot write to the destination because enforce_in_transit is set to true and the destination locations are not in the allowed regions. + SCHEMA_MISMATCH (5): + Cannot write to the Cloud Storage bucket due + to an incompatibility between the topic schema + and subscription settings. """ STATE_UNSPECIFIED = 0 ACTIVE = 1 PERMISSION_DENIED = 2 NOT_FOUND = 3 IN_TRANSIT_LOCATION_RESTRICTION = 4 + SCHEMA_MISMATCH = 5 class TextConfig(proto.Message): r"""Configuration for writing message data in text format. @@ -1473,12 +1499,20 @@ class AvroConfig(proto.Message): fields while all other message properties other than data (for example, an ordering_key, if present) are added as entries in the attributes map. + use_topic_schema (bool): + Optional. When true, the output Cloud Storage + file will be serialized using the topic schema, + if it exists. """ write_metadata: bool = proto.Field( proto.BOOL, number=1, ) + use_topic_schema: bool = proto.Field( + proto.BOOL, + number=2, + ) bucket: str = proto.Field( proto.STRING, @@ -1522,6 +1556,10 @@ class AvroConfig(proto.Message): number=9, enum=State, ) + service_account_email: str = proto.Field( + proto.STRING, + number=11, + ) class ReceivedMessage(proto.Message): diff --git a/owlbot.py b/owlbot.py index 3551413ee..204b30ba5 100644 --- a/owlbot.py +++ b/owlbot.py @@ -101,16 +101,16 @@ count = s.replace( clients_to_patch, - r"Transport = type\(self\)\.get_transport_class\(cast\(str, transport\)\)", + r"# initialize with the provided callable or the passed in class", """\g<0> emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") if emulator_host: - if issubclass(Transport, type(self)._transport_registry["grpc"]): + if issubclass(transport_init, type(self)._transport_registry["grpc"]): channel = grpc.insecure_channel(target=emulator_host) else: channel = grpc.aio.insecure_channel(target=emulator_host) - Transport = functools.partial(Transport, channel=channel) + transport_init = functools.partial(transport_init, channel=channel) """, ) diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 2ab8d41b7..d66015ac4 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.21.5" + "version": "0.1.0" }, "snippets": [ { diff --git a/tests/unit/gapic/pubsub_v1/test_publisher.py b/tests/unit/gapic/pubsub_v1/test_publisher.py index 20cecf1c9..6f1c95722 100644 --- a/tests/unit/gapic/pubsub_v1/test_publisher.py +++ b/tests/unit/gapic/pubsub_v1/test_publisher.py @@ -1138,6 +1138,9 @@ def test_create_topic_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.create_topic), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.create_topic() call.assert_called() _, args, _ = call.mock_calls[0] @@ -1162,6 +1165,9 @@ def test_create_topic_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.create_topic), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.create_topic(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -1171,6 +1177,41 @@ def test_create_topic_non_empty_request_with_auto_populated_field(): ) +def test_create_topic_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.create_topic in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.create_topic] = mock_rpc + request = {} + client.create_topic(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.create_topic(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_create_topic_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -1197,6 +1238,47 @@ async def test_create_topic_empty_call_async(): assert args[0] == pubsub.Topic() +@pytest.mark.asyncio +async def test_create_topic_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.create_topic + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.create_topic + ] = mock_object + + request = {} + await client.create_topic(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.create_topic(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_create_topic_async( transport: str = "grpc_asyncio", request_type=pubsub.Topic @@ -1433,6 +1515,9 @@ def test_update_topic_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.update_topic), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.update_topic() call.assert_called() _, args, _ = call.mock_calls[0] @@ -1454,12 +1539,50 @@ def test_update_topic_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.update_topic), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.update_topic(request=request) call.assert_called() _, args, _ = call.mock_calls[0] assert args[0] == pubsub.UpdateTopicRequest() +def test_update_topic_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.update_topic in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.update_topic] = mock_rpc + request = {} + client.update_topic(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.update_topic(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_update_topic_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -1486,6 +1609,47 @@ async def test_update_topic_empty_call_async(): assert args[0] == pubsub.UpdateTopicRequest() +@pytest.mark.asyncio +async def test_update_topic_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.update_topic + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.update_topic + ] = mock_object + + request = {} + await client.update_topic(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.update_topic(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_update_topic_async( transport: str = "grpc_asyncio", request_type=pubsub.UpdateTopicRequest @@ -1726,6 +1890,9 @@ def test_publish_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.publish), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.publish() call.assert_called() _, args, _ = call.mock_calls[0] @@ -1749,6 +1916,9 @@ def test_publish_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.publish), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.publish(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -1757,6 +1927,41 @@ def test_publish_non_empty_request_with_auto_populated_field(): ) +def test_publish_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.publish in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.publish] = mock_rpc + request = {} + client.publish(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.publish(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_publish_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -1780,6 +1985,45 @@ async def test_publish_empty_call_async(): assert args[0] == pubsub.PublishRequest() +@pytest.mark.asyncio +async def test_publish_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio"): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.publish + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.publish + ] = mock_object + + request = {} + await client.publish(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.publish(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_publish_async( transport: str = "grpc_asyncio", request_type=pubsub.PublishRequest @@ -2024,6 +2268,9 @@ def test_get_topic_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.get_topic), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.get_topic() call.assert_called() _, args, _ = call.mock_calls[0] @@ -2047,6 +2294,9 @@ def test_get_topic_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.get_topic), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.get_topic(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -2055,6 +2305,41 @@ def test_get_topic_non_empty_request_with_auto_populated_field(): ) +def test_get_topic_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.get_topic in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.get_topic] = mock_rpc + request = {} + client.get_topic(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.get_topic(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_get_topic_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -2081,6 +2366,45 @@ async def test_get_topic_empty_call_async(): assert args[0] == pubsub.GetTopicRequest() +@pytest.mark.asyncio +async def test_get_topic_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio"): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.get_topic + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.get_topic + ] = mock_object + + request = {} + await client.get_topic(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.get_topic(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_get_topic_async( transport: str = "grpc_asyncio", request_type=pubsub.GetTopicRequest @@ -2311,6 +2635,9 @@ def test_list_topics_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.list_topics), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.list_topics() call.assert_called() _, args, _ = call.mock_calls[0] @@ -2335,6 +2662,9 @@ def test_list_topics_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.list_topics), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.list_topics(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -2344,6 +2674,41 @@ def test_list_topics_non_empty_request_with_auto_populated_field(): ) +def test_list_topics_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.list_topics in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.list_topics] = mock_rpc + request = {} + client.list_topics(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.list_topics(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_list_topics_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -2367,6 +2732,47 @@ async def test_list_topics_empty_call_async(): assert args[0] == pubsub.ListTopicsRequest() +@pytest.mark.asyncio +async def test_list_topics_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.list_topics + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.list_topics + ] = mock_object + + request = {} + await client.list_topics(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.list_topics(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_list_topics_async( transport: str = "grpc_asyncio", request_type=pubsub.ListTopicsRequest @@ -2586,13 +2992,13 @@ def test_list_topics_pager(transport_name: str = "grpc"): RuntimeError, ) - metadata = () - metadata = tuple(metadata) + ( + expected_metadata = () + expected_metadata = tuple(expected_metadata) + ( gapic_v1.routing_header.to_grpc_metadata((("project", ""),)), ) pager = client.list_topics(request={}) - assert pager._metadata == metadata + assert pager._metadata == expected_metadata results = list(pager) assert len(results) == 6 @@ -2791,6 +3197,9 @@ def test_list_topic_subscriptions_empty_call(): with mock.patch.object( type(client.transport.list_topic_subscriptions), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.list_topic_subscriptions() call.assert_called() _, args, _ = call.mock_calls[0] @@ -2817,6 +3226,9 @@ def test_list_topic_subscriptions_non_empty_request_with_auto_populated_field(): with mock.patch.object( type(client.transport.list_topic_subscriptions), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.list_topic_subscriptions(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -2826,6 +3238,46 @@ def test_list_topic_subscriptions_non_empty_request_with_auto_populated_field(): ) +def test_list_topic_subscriptions_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.list_topic_subscriptions + in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.list_topic_subscriptions + ] = mock_rpc + request = {} + client.list_topic_subscriptions(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.list_topic_subscriptions(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_list_topic_subscriptions_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -2852,6 +3304,47 @@ async def test_list_topic_subscriptions_empty_call_async(): assert args[0] == pubsub.ListTopicSubscriptionsRequest() +@pytest.mark.asyncio +async def test_list_topic_subscriptions_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.list_topic_subscriptions + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.list_topic_subscriptions + ] = mock_object + + request = {} + await client.list_topic_subscriptions(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.list_topic_subscriptions(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_list_topic_subscriptions_async( transport: str = "grpc_asyncio", request_type=pubsub.ListTopicSubscriptionsRequest @@ -3085,13 +3578,13 @@ def test_list_topic_subscriptions_pager(transport_name: str = "grpc"): RuntimeError, ) - metadata = () - metadata = tuple(metadata) + ( + expected_metadata = () + expected_metadata = tuple(expected_metadata) + ( gapic_v1.routing_header.to_grpc_metadata((("topic", ""),)), ) pager = client.list_topic_subscriptions(request={}) - assert pager._metadata == metadata + assert pager._metadata == expected_metadata results = list(pager) assert len(results) == 6 @@ -3296,6 +3789,9 @@ def test_list_topic_snapshots_empty_call(): with mock.patch.object( type(client.transport.list_topic_snapshots), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.list_topic_snapshots() call.assert_called() _, args, _ = call.mock_calls[0] @@ -3322,6 +3818,9 @@ def test_list_topic_snapshots_non_empty_request_with_auto_populated_field(): with mock.patch.object( type(client.transport.list_topic_snapshots), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.list_topic_snapshots(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -3331,6 +3830,45 @@ def test_list_topic_snapshots_non_empty_request_with_auto_populated_field(): ) +def test_list_topic_snapshots_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.list_topic_snapshots in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.list_topic_snapshots + ] = mock_rpc + request = {} + client.list_topic_snapshots(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.list_topic_snapshots(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_list_topic_snapshots_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -3357,6 +3895,47 @@ async def test_list_topic_snapshots_empty_call_async(): assert args[0] == pubsub.ListTopicSnapshotsRequest() +@pytest.mark.asyncio +async def test_list_topic_snapshots_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.list_topic_snapshots + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.list_topic_snapshots + ] = mock_object + + request = {} + await client.list_topic_snapshots(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.list_topic_snapshots(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_list_topic_snapshots_async( transport: str = "grpc_asyncio", request_type=pubsub.ListTopicSnapshotsRequest @@ -3590,13 +4169,13 @@ def test_list_topic_snapshots_pager(transport_name: str = "grpc"): RuntimeError, ) - metadata = () - metadata = tuple(metadata) + ( + expected_metadata = () + expected_metadata = tuple(expected_metadata) + ( gapic_v1.routing_header.to_grpc_metadata((("topic", ""),)), ) pager = client.list_topic_snapshots(request={}) - assert pager._metadata == metadata + assert pager._metadata == expected_metadata results = list(pager) assert len(results) == 6 @@ -3792,6 +4371,9 @@ def test_delete_topic_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.delete_topic), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.delete_topic() call.assert_called() _, args, _ = call.mock_calls[0] @@ -3815,6 +4397,9 @@ def test_delete_topic_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.delete_topic), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.delete_topic(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -3823,6 +4408,41 @@ def test_delete_topic_non_empty_request_with_auto_populated_field(): ) +def test_delete_topic_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.delete_topic in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.delete_topic] = mock_rpc + request = {} + client.delete_topic(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.delete_topic(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_delete_topic_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -3842,6 +4462,47 @@ async def test_delete_topic_empty_call_async(): assert args[0] == pubsub.DeleteTopicRequest() +@pytest.mark.asyncio +async def test_delete_topic_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.delete_topic + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.delete_topic + ] = mock_object + + request = {} + await client.delete_topic(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.delete_topic(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_delete_topic_async( transport: str = "grpc_asyncio", request_type=pubsub.DeleteTopicRequest @@ -4062,6 +4723,9 @@ def test_detach_subscription_empty_call(): with mock.patch.object( type(client.transport.detach_subscription), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.detach_subscription() call.assert_called() _, args, _ = call.mock_calls[0] @@ -4087,6 +4751,9 @@ def test_detach_subscription_non_empty_request_with_auto_populated_field(): with mock.patch.object( type(client.transport.detach_subscription), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.detach_subscription(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -4095,6 +4762,45 @@ def test_detach_subscription_non_empty_request_with_auto_populated_field(): ) +def test_detach_subscription_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.detach_subscription in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.detach_subscription + ] = mock_rpc + request = {} + client.detach_subscription(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.detach_subscription(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_detach_subscription_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -4118,6 +4824,47 @@ async def test_detach_subscription_empty_call_async(): assert args[0] == pubsub.DetachSubscriptionRequest() +@pytest.mark.asyncio +async def test_detach_subscription_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = PublisherAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.detach_subscription + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.detach_subscription + ] = mock_object + + request = {} + await client.detach_subscription(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.detach_subscription(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_detach_subscription_async( transport: str = "grpc_asyncio", request_type=pubsub.DetachSubscriptionRequest @@ -4267,6 +5014,42 @@ def test_create_topic_rest(request_type): assert response.state == pubsub.Topic.State.ACTIVE +def test_create_topic_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.create_topic in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.create_topic] = mock_rpc + + request = {} + client.create_topic(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.create_topic(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_create_topic_rest_required_fields(request_type=pubsub.Topic): transport_class = transports.PublisherRestTransport @@ -4531,6 +5314,42 @@ def test_update_topic_rest(request_type): assert response.state == pubsub.Topic.State.ACTIVE +def test_update_topic_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.update_topic in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.update_topic] = mock_rpc + + request = {} + client.update_topic(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.update_topic(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_update_topic_rest_required_fields(request_type=pubsub.UpdateTopicRequest): transport_class = transports.PublisherRestTransport @@ -4794,6 +5613,42 @@ def test_publish_rest(request_type): assert response.message_ids == ["message_ids_value"] +def test_publish_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.publish in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.publish] = mock_rpc + + request = {} + client.publish(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.publish(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_publish_rest_required_fields(request_type=pubsub.PublishRequest): transport_class = transports.PublisherRestTransport @@ -5071,6 +5926,42 @@ def test_get_topic_rest(request_type): assert response.state == pubsub.Topic.State.ACTIVE +def test_get_topic_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.get_topic in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.get_topic] = mock_rpc + + request = {} + client.get_topic(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.get_topic(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_get_topic_rest_required_fields(request_type=pubsub.GetTopicRequest): transport_class = transports.PublisherRestTransport @@ -5328,6 +6219,42 @@ def test_list_topics_rest(request_type): assert response.next_page_token == "next_page_token_value" +def test_list_topics_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.list_topics in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.list_topics] = mock_rpc + + request = {} + client.list_topics(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.list_topics(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_list_topics_rest_required_fields(request_type=pubsub.ListTopicsRequest): transport_class = transports.PublisherRestTransport @@ -5659,6 +6586,47 @@ def test_list_topic_subscriptions_rest(request_type): assert response.next_page_token == "next_page_token_value" +def test_list_topic_subscriptions_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.list_topic_subscriptions + in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.list_topic_subscriptions + ] = mock_rpc + + request = {} + client.list_topic_subscriptions(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.list_topic_subscriptions(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_list_topic_subscriptions_rest_required_fields( request_type=pubsub.ListTopicSubscriptionsRequest, ): @@ -5997,6 +6965,46 @@ def test_list_topic_snapshots_rest(request_type): assert response.next_page_token == "next_page_token_value" +def test_list_topic_snapshots_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.list_topic_snapshots in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.list_topic_snapshots + ] = mock_rpc + + request = {} + client.list_topic_snapshots(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.list_topic_snapshots(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_list_topic_snapshots_rest_required_fields( request_type=pubsub.ListTopicSnapshotsRequest, ): @@ -6326,6 +7334,42 @@ def test_delete_topic_rest(request_type): assert response is None +def test_delete_topic_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.delete_topic in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.delete_topic] = mock_rpc + + request = {} + client.delete_topic(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.delete_topic(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_delete_topic_rest_required_fields(request_type=pubsub.DeleteTopicRequest): transport_class = transports.PublisherRestTransport @@ -6569,6 +7613,46 @@ def test_detach_subscription_rest(request_type): assert isinstance(response, pubsub.DetachSubscriptionResponse) +def test_detach_subscription_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.detach_subscription in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.detach_subscription + ] = mock_rpc + + request = {} + client.detach_subscription(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.detach_subscription(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_detach_subscription_rest_required_fields( request_type=pubsub.DetachSubscriptionRequest, ): diff --git a/tests/unit/gapic/pubsub_v1/test_schema_service.py b/tests/unit/gapic/pubsub_v1/test_schema_service.py index 5de9c6c45..f44f6846f 100644 --- a/tests/unit/gapic/pubsub_v1/test_schema_service.py +++ b/tests/unit/gapic/pubsub_v1/test_schema_service.py @@ -1177,6 +1177,9 @@ def test_create_schema_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.create_schema), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.create_schema() call.assert_called() _, args, _ = call.mock_calls[0] @@ -1201,6 +1204,9 @@ def test_create_schema_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.create_schema), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.create_schema(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -1210,6 +1216,41 @@ def test_create_schema_non_empty_request_with_auto_populated_field(): ) +def test_create_schema_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.create_schema in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.create_schema] = mock_rpc + request = {} + client.create_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.create_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_create_schema_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -1236,6 +1277,47 @@ async def test_create_schema_empty_call_async(): assert args[0] == gp_schema.CreateSchemaRequest() +@pytest.mark.asyncio +async def test_create_schema_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.create_schema + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.create_schema + ] = mock_object + + request = {} + await client.create_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.create_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_create_schema_async( transport: str = "grpc_asyncio", request_type=gp_schema.CreateSchemaRequest @@ -1492,6 +1574,9 @@ def test_get_schema_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.get_schema), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.get_schema() call.assert_called() _, args, _ = call.mock_calls[0] @@ -1515,6 +1600,9 @@ def test_get_schema_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.get_schema), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.get_schema(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -1523,6 +1611,41 @@ def test_get_schema_non_empty_request_with_auto_populated_field(): ) +def test_get_schema_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.get_schema in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.get_schema] = mock_rpc + request = {} + client.get_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.get_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_get_schema_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -1549,6 +1672,45 @@ async def test_get_schema_empty_call_async(): assert args[0] == schema.GetSchemaRequest() +@pytest.mark.asyncio +async def test_get_schema_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio"): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.get_schema + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.get_schema + ] = mock_object + + request = {} + await client.get_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.get_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_get_schema_async( transport: str = "grpc_asyncio", request_type=schema.GetSchemaRequest @@ -1779,6 +1941,9 @@ def test_list_schemas_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.list_schemas), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.list_schemas() call.assert_called() _, args, _ = call.mock_calls[0] @@ -1803,6 +1968,9 @@ def test_list_schemas_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.list_schemas), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.list_schemas(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -1812,6 +1980,41 @@ def test_list_schemas_non_empty_request_with_auto_populated_field(): ) +def test_list_schemas_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.list_schemas in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.list_schemas] = mock_rpc + request = {} + client.list_schemas(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.list_schemas(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_list_schemas_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -1835,6 +2038,47 @@ async def test_list_schemas_empty_call_async(): assert args[0] == schema.ListSchemasRequest() +@pytest.mark.asyncio +async def test_list_schemas_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.list_schemas + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.list_schemas + ] = mock_object + + request = {} + await client.list_schemas(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.list_schemas(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_list_schemas_async( transport: str = "grpc_asyncio", request_type=schema.ListSchemasRequest @@ -2054,13 +2298,13 @@ def test_list_schemas_pager(transport_name: str = "grpc"): RuntimeError, ) - metadata = () - metadata = tuple(metadata) + ( + expected_metadata = () + expected_metadata = tuple(expected_metadata) + ( gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), ) pager = client.list_schemas(request={}) - assert pager._metadata == metadata + assert pager._metadata == expected_metadata results = list(pager) assert len(results) == 6 @@ -2257,6 +2501,9 @@ def test_list_schema_revisions_empty_call(): with mock.patch.object( type(client.transport.list_schema_revisions), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.list_schema_revisions() call.assert_called() _, args, _ = call.mock_calls[0] @@ -2283,6 +2530,9 @@ def test_list_schema_revisions_non_empty_request_with_auto_populated_field(): with mock.patch.object( type(client.transport.list_schema_revisions), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.list_schema_revisions(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -2292,6 +2542,46 @@ def test_list_schema_revisions_non_empty_request_with_auto_populated_field(): ) +def test_list_schema_revisions_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.list_schema_revisions + in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.list_schema_revisions + ] = mock_rpc + request = {} + client.list_schema_revisions(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.list_schema_revisions(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_list_schema_revisions_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -2317,6 +2607,47 @@ async def test_list_schema_revisions_empty_call_async(): assert args[0] == schema.ListSchemaRevisionsRequest() +@pytest.mark.asyncio +async def test_list_schema_revisions_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.list_schema_revisions + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.list_schema_revisions + ] = mock_object + + request = {} + await client.list_schema_revisions(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.list_schema_revisions(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_list_schema_revisions_async( transport: str = "grpc_asyncio", request_type=schema.ListSchemaRevisionsRequest @@ -2548,13 +2879,13 @@ def test_list_schema_revisions_pager(transport_name: str = "grpc"): RuntimeError, ) - metadata = () - metadata = tuple(metadata) + ( + expected_metadata = () + expected_metadata = tuple(expected_metadata) + ( gapic_v1.routing_header.to_grpc_metadata((("name", ""),)), ) pager = client.list_schema_revisions(request={}) - assert pager._metadata == metadata + assert pager._metadata == expected_metadata results = list(pager) assert len(results) == 6 @@ -2759,6 +3090,9 @@ def test_commit_schema_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.commit_schema), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.commit_schema() call.assert_called() _, args, _ = call.mock_calls[0] @@ -2782,6 +3116,9 @@ def test_commit_schema_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.commit_schema), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.commit_schema(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -2790,6 +3127,41 @@ def test_commit_schema_non_empty_request_with_auto_populated_field(): ) +def test_commit_schema_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.commit_schema in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.commit_schema] = mock_rpc + request = {} + client.commit_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.commit_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_commit_schema_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -2816,6 +3188,47 @@ async def test_commit_schema_empty_call_async(): assert args[0] == gp_schema.CommitSchemaRequest() +@pytest.mark.asyncio +async def test_commit_schema_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.commit_schema + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.commit_schema + ] = mock_object + + request = {} + await client.commit_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.commit_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_commit_schema_async( transport: str = "grpc_asyncio", request_type=gp_schema.CommitSchemaRequest @@ -3062,6 +3475,9 @@ def test_rollback_schema_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.rollback_schema), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.rollback_schema() call.assert_called() _, args, _ = call.mock_calls[0] @@ -3086,6 +3502,9 @@ def test_rollback_schema_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.rollback_schema), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.rollback_schema(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -3095,6 +3514,41 @@ def test_rollback_schema_non_empty_request_with_auto_populated_field(): ) +def test_rollback_schema_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.rollback_schema in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.rollback_schema] = mock_rpc + request = {} + client.rollback_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.rollback_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_rollback_schema_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -3121,6 +3575,47 @@ async def test_rollback_schema_empty_call_async(): assert args[0] == schema.RollbackSchemaRequest() +@pytest.mark.asyncio +async def test_rollback_schema_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.rollback_schema + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.rollback_schema + ] = mock_object + + request = {} + await client.rollback_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.rollback_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_rollback_schema_async( transport: str = "grpc_asyncio", request_type=schema.RollbackSchemaRequest @@ -3371,6 +3866,9 @@ def test_delete_schema_revision_empty_call(): with mock.patch.object( type(client.transport.delete_schema_revision), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.delete_schema_revision() call.assert_called() _, args, _ = call.mock_calls[0] @@ -3397,6 +3895,9 @@ def test_delete_schema_revision_non_empty_request_with_auto_populated_field(): with mock.patch.object( type(client.transport.delete_schema_revision), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.delete_schema_revision(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -3406,6 +3907,46 @@ def test_delete_schema_revision_non_empty_request_with_auto_populated_field(): ) +def test_delete_schema_revision_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.delete_schema_revision + in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.delete_schema_revision + ] = mock_rpc + request = {} + client.delete_schema_revision(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.delete_schema_revision(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_delete_schema_revision_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -3434,6 +3975,47 @@ async def test_delete_schema_revision_empty_call_async(): assert args[0] == schema.DeleteSchemaRevisionRequest() +@pytest.mark.asyncio +async def test_delete_schema_revision_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.delete_schema_revision + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.delete_schema_revision + ] = mock_object + + request = {} + await client.delete_schema_revision(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.delete_schema_revision(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_delete_schema_revision_async( transport: str = "grpc_asyncio", request_type=schema.DeleteSchemaRevisionRequest @@ -3681,6 +4263,9 @@ def test_delete_schema_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.delete_schema), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.delete_schema() call.assert_called() _, args, _ = call.mock_calls[0] @@ -3704,6 +4289,9 @@ def test_delete_schema_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.delete_schema), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.delete_schema(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -3712,6 +4300,41 @@ def test_delete_schema_non_empty_request_with_auto_populated_field(): ) +def test_delete_schema_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.delete_schema in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.delete_schema] = mock_rpc + request = {} + client.delete_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.delete_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_delete_schema_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -3731,6 +4354,47 @@ async def test_delete_schema_empty_call_async(): assert args[0] == schema.DeleteSchemaRequest() +@pytest.mark.asyncio +async def test_delete_schema_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.delete_schema + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.delete_schema + ] = mock_object + + request = {} + await client.delete_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.delete_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_delete_schema_async( transport: str = "grpc_asyncio", request_type=schema.DeleteSchemaRequest @@ -3947,6 +4611,9 @@ def test_validate_schema_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.validate_schema), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.validate_schema() call.assert_called() _, args, _ = call.mock_calls[0] @@ -3970,6 +4637,9 @@ def test_validate_schema_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.validate_schema), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.validate_schema(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -3978,6 +4648,41 @@ def test_validate_schema_non_empty_request_with_auto_populated_field(): ) +def test_validate_schema_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.validate_schema in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.validate_schema] = mock_rpc + request = {} + client.validate_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.validate_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_validate_schema_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -3999,6 +4704,47 @@ async def test_validate_schema_empty_call_async(): assert args[0] == gp_schema.ValidateSchemaRequest() +@pytest.mark.asyncio +async def test_validate_schema_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.validate_schema + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.validate_schema + ] = mock_object + + request = {} + await client.validate_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.validate_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_validate_schema_async( transport: str = "grpc_asyncio", request_type=gp_schema.ValidateSchemaRequest @@ -4231,6 +4977,9 @@ def test_validate_message_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.validate_message), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.validate_message() call.assert_called() _, args, _ = call.mock_calls[0] @@ -4255,6 +5004,9 @@ def test_validate_message_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.validate_message), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.validate_message(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -4264,6 +5016,43 @@ def test_validate_message_non_empty_request_with_auto_populated_field(): ) +def test_validate_message_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.validate_message in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.validate_message + ] = mock_rpc + request = {} + client.validate_message(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.validate_message(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_validate_message_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -4285,6 +5074,47 @@ async def test_validate_message_empty_call_async(): assert args[0] == schema.ValidateMessageRequest() +@pytest.mark.asyncio +async def test_validate_message_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SchemaServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.validate_message + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.validate_message + ] = mock_object + + request = {} + await client.validate_message(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.validate_message(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_validate_message_async( transport: str = "grpc_asyncio", request_type=schema.ValidateMessageRequest @@ -4502,6 +5332,42 @@ def get_message_fields(field): assert response.revision_id == "revision_id_value" +def test_create_schema_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.create_schema in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.create_schema] = mock_rpc + + request = {} + client.create_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.create_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_create_schema_rest_required_fields(request_type=gp_schema.CreateSchemaRequest): transport_class = transports.SchemaServiceRestTransport @@ -4782,6 +5648,42 @@ def test_get_schema_rest(request_type): assert response.revision_id == "revision_id_value" +def test_get_schema_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.get_schema in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.get_schema] = mock_rpc + + request = {} + client.get_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.get_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_get_schema_rest_required_fields(request_type=schema.GetSchemaRequest): transport_class = transports.SchemaServiceRestTransport @@ -5043,6 +5945,42 @@ def test_list_schemas_rest(request_type): assert response.next_page_token == "next_page_token_value" +def test_list_schemas_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.list_schemas in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.list_schemas] = mock_rpc + + request = {} + client.list_schemas(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.list_schemas(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_list_schemas_rest_required_fields(request_type=schema.ListSchemasRequest): transport_class = transports.SchemaServiceRestTransport @@ -5376,6 +6314,47 @@ def test_list_schema_revisions_rest(request_type): assert response.next_page_token == "next_page_token_value" +def test_list_schema_revisions_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.list_schema_revisions + in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.list_schema_revisions + ] = mock_rpc + + request = {} + client.list_schema_revisions(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.list_schema_revisions(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_list_schema_revisions_rest_required_fields( request_type=schema.ListSchemaRevisionsRequest, ): @@ -5722,6 +6701,42 @@ def test_commit_schema_rest(request_type): assert response.revision_id == "revision_id_value" +def test_commit_schema_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.commit_schema in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.commit_schema] = mock_rpc + + request = {} + client.commit_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.commit_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_commit_schema_rest_required_fields(request_type=gp_schema.CommitSchemaRequest): transport_class = transports.SchemaServiceRestTransport @@ -5998,6 +7013,42 @@ def test_rollback_schema_rest(request_type): assert response.revision_id == "revision_id_value" +def test_rollback_schema_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.rollback_schema in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.rollback_schema] = mock_rpc + + request = {} + client.rollback_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.rollback_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_rollback_schema_rest_required_fields( request_type=schema.RollbackSchemaRequest, ): @@ -6281,6 +7332,47 @@ def test_delete_schema_revision_rest(request_type): assert response.revision_id == "revision_id_value" +def test_delete_schema_revision_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.delete_schema_revision + in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.delete_schema_revision + ] = mock_rpc + + request = {} + client.delete_schema_revision(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.delete_schema_revision(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_delete_schema_revision_rest_required_fields( request_type=schema.DeleteSchemaRevisionRequest, ): @@ -6544,6 +7636,42 @@ def test_delete_schema_rest(request_type): assert response is None +def test_delete_schema_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.delete_schema in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.delete_schema] = mock_rpc + + request = {} + client.delete_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.delete_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_delete_schema_rest_required_fields(request_type=schema.DeleteSchemaRequest): transport_class = transports.SchemaServiceRestTransport @@ -6789,6 +7917,42 @@ def test_validate_schema_rest(request_type): assert isinstance(response, gp_schema.ValidateSchemaResponse) +def test_validate_schema_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.validate_schema in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.validate_schema] = mock_rpc + + request = {} + client.validate_schema(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.validate_schema(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_validate_schema_rest_required_fields( request_type=gp_schema.ValidateSchemaRequest, ): @@ -7063,6 +8227,44 @@ def test_validate_message_rest(request_type): assert isinstance(response, schema.ValidateMessageResponse) +def test_validate_message_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.validate_message in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.validate_message + ] = mock_rpc + + request = {} + client.validate_message(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.validate_message(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_validate_message_rest_required_fields( request_type=schema.ValidateMessageRequest, ): diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py index 50e02b52d..0251bda96 100644 --- a/tests/unit/gapic/pubsub_v1/test_subscriber.py +++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py @@ -1156,6 +1156,9 @@ def test_create_subscription_empty_call(): with mock.patch.object( type(client.transport.create_subscription), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.create_subscription() call.assert_called() _, args, _ = call.mock_calls[0] @@ -1183,6 +1186,9 @@ def test_create_subscription_non_empty_request_with_auto_populated_field(): with mock.patch.object( type(client.transport.create_subscription), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.create_subscription(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -1193,6 +1199,45 @@ def test_create_subscription_non_empty_request_with_auto_populated_field(): ) +def test_create_subscription_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.create_subscription in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.create_subscription + ] = mock_rpc + request = {} + client.create_subscription(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.create_subscription(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_create_subscription_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -1226,6 +1271,47 @@ async def test_create_subscription_empty_call_async(): assert args[0] == pubsub.Subscription() +@pytest.mark.asyncio +async def test_create_subscription_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.create_subscription + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.create_subscription + ] = mock_object + + request = {} + await client.create_subscription(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.create_subscription(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_create_subscription_async( transport: str = "grpc_asyncio", request_type=pubsub.Subscription @@ -1522,6 +1608,9 @@ def test_get_subscription_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.get_subscription), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.get_subscription() call.assert_called() _, args, _ = call.mock_calls[0] @@ -1545,6 +1634,9 @@ def test_get_subscription_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.get_subscription), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.get_subscription(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -1553,6 +1645,43 @@ def test_get_subscription_non_empty_request_with_auto_populated_field(): ) +def test_get_subscription_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.get_subscription in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.get_subscription + ] = mock_rpc + request = {} + client.get_subscription(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.get_subscription(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_get_subscription_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -1584,6 +1713,47 @@ async def test_get_subscription_empty_call_async(): assert args[0] == pubsub.GetSubscriptionRequest() +@pytest.mark.asyncio +async def test_get_subscription_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.get_subscription + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.get_subscription + ] = mock_object + + request = {} + await client.get_subscription(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.get_subscription(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_get_subscription_async( transport: str = "grpc_asyncio", request_type=pubsub.GetSubscriptionRequest @@ -1844,6 +2014,9 @@ def test_update_subscription_empty_call(): with mock.patch.object( type(client.transport.update_subscription), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.update_subscription() call.assert_called() _, args, _ = call.mock_calls[0] @@ -1867,12 +2040,54 @@ def test_update_subscription_non_empty_request_with_auto_populated_field(): with mock.patch.object( type(client.transport.update_subscription), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.update_subscription(request=request) call.assert_called() _, args, _ = call.mock_calls[0] assert args[0] == pubsub.UpdateSubscriptionRequest() +def test_update_subscription_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.update_subscription in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.update_subscription + ] = mock_rpc + request = {} + client.update_subscription(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.update_subscription(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_update_subscription_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -1906,6 +2121,47 @@ async def test_update_subscription_empty_call_async(): assert args[0] == pubsub.UpdateSubscriptionRequest() +@pytest.mark.asyncio +async def test_update_subscription_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.update_subscription + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.update_subscription + ] = mock_object + + request = {} + await client.update_subscription(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.update_subscription(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_update_subscription_async( transport: str = "grpc_asyncio", request_type=pubsub.UpdateSubscriptionRequest @@ -2170,6 +2426,9 @@ def test_list_subscriptions_empty_call(): with mock.patch.object( type(client.transport.list_subscriptions), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.list_subscriptions() call.assert_called() _, args, _ = call.mock_calls[0] @@ -2196,6 +2455,9 @@ def test_list_subscriptions_non_empty_request_with_auto_populated_field(): with mock.patch.object( type(client.transport.list_subscriptions), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.list_subscriptions(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -2205,6 +2467,45 @@ def test_list_subscriptions_non_empty_request_with_auto_populated_field(): ) +def test_list_subscriptions_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.list_subscriptions in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.list_subscriptions + ] = mock_rpc + request = {} + client.list_subscriptions(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.list_subscriptions(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_list_subscriptions_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -2230,6 +2531,47 @@ async def test_list_subscriptions_empty_call_async(): assert args[0] == pubsub.ListSubscriptionsRequest() +@pytest.mark.asyncio +async def test_list_subscriptions_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.list_subscriptions + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.list_subscriptions + ] = mock_object + + request = {} + await client.list_subscriptions(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.list_subscriptions(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_list_subscriptions_async( transport: str = "grpc_asyncio", request_type=pubsub.ListSubscriptionsRequest @@ -2461,13 +2803,13 @@ def test_list_subscriptions_pager(transport_name: str = "grpc"): RuntimeError, ) - metadata = () - metadata = tuple(metadata) + ( + expected_metadata = () + expected_metadata = tuple(expected_metadata) + ( gapic_v1.routing_header.to_grpc_metadata((("project", ""),)), ) pager = client.list_subscriptions(request={}) - assert pager._metadata == metadata + assert pager._metadata == expected_metadata results = list(pager) assert len(results) == 6 @@ -2667,6 +3009,9 @@ def test_delete_subscription_empty_call(): with mock.patch.object( type(client.transport.delete_subscription), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.delete_subscription() call.assert_called() _, args, _ = call.mock_calls[0] @@ -2692,6 +3037,9 @@ def test_delete_subscription_non_empty_request_with_auto_populated_field(): with mock.patch.object( type(client.transport.delete_subscription), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.delete_subscription(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -2700,6 +3048,45 @@ def test_delete_subscription_non_empty_request_with_auto_populated_field(): ) +def test_delete_subscription_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.delete_subscription in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.delete_subscription + ] = mock_rpc + request = {} + client.delete_subscription(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.delete_subscription(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_delete_subscription_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -2721,6 +3108,47 @@ async def test_delete_subscription_empty_call_async(): assert args[0] == pubsub.DeleteSubscriptionRequest() +@pytest.mark.asyncio +async def test_delete_subscription_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.delete_subscription + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.delete_subscription + ] = mock_object + + request = {} + await client.delete_subscription(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.delete_subscription(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_delete_subscription_async( transport: str = "grpc_asyncio", request_type=pubsub.DeleteSubscriptionRequest @@ -2951,6 +3379,9 @@ def test_modify_ack_deadline_empty_call(): with mock.patch.object( type(client.transport.modify_ack_deadline), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.modify_ack_deadline() call.assert_called() _, args, _ = call.mock_calls[0] @@ -2976,6 +3407,9 @@ def test_modify_ack_deadline_non_empty_request_with_auto_populated_field(): with mock.patch.object( type(client.transport.modify_ack_deadline), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.modify_ack_deadline(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -2984,6 +3418,45 @@ def test_modify_ack_deadline_non_empty_request_with_auto_populated_field(): ) +def test_modify_ack_deadline_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.modify_ack_deadline in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.modify_ack_deadline + ] = mock_rpc + request = {} + client.modify_ack_deadline(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.modify_ack_deadline(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_modify_ack_deadline_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -3005,6 +3478,47 @@ async def test_modify_ack_deadline_empty_call_async(): assert args[0] == pubsub.ModifyAckDeadlineRequest() +@pytest.mark.asyncio +async def test_modify_ack_deadline_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.modify_ack_deadline + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.modify_ack_deadline + ] = mock_object + + request = {} + await client.modify_ack_deadline(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.modify_ack_deadline(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_modify_ack_deadline_async( transport: str = "grpc_asyncio", request_type=pubsub.ModifyAckDeadlineRequest @@ -3251,6 +3765,9 @@ def test_acknowledge_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.acknowledge), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.acknowledge() call.assert_called() _, args, _ = call.mock_calls[0] @@ -3274,6 +3791,9 @@ def test_acknowledge_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.acknowledge), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.acknowledge(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -3282,6 +3802,41 @@ def test_acknowledge_non_empty_request_with_auto_populated_field(): ) +def test_acknowledge_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.acknowledge in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.acknowledge] = mock_rpc + request = {} + client.acknowledge(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.acknowledge(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_acknowledge_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -3302,19 +3857,60 @@ async def test_acknowledge_empty_call_async(): @pytest.mark.asyncio -async def test_acknowledge_async( - transport: str = "grpc_asyncio", request_type=pubsub.AcknowledgeRequest +async def test_acknowledge_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", ): - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) - # Everything is optional in proto3 as far as the runtime is concerned, - # and we are mocking out the actual API, so just send an empty request. - request = request_type() + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() - # Mock the actual call within the gRPC stub, and fake the request. + # Ensure method has been cached + assert ( + client._client._transport.acknowledge + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.acknowledge + ] = mock_object + + request = {} + await client.acknowledge(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.acknowledge(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + +@pytest.mark.asyncio +async def test_acknowledge_async( + transport: str = "grpc_asyncio", request_type=pubsub.AcknowledgeRequest +): + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.acknowledge), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -3527,6 +4123,9 @@ def test_pull_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.pull), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.pull() call.assert_called() _, args, _ = call.mock_calls[0] @@ -3550,6 +4149,9 @@ def test_pull_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.pull), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.pull(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -3558,6 +4160,41 @@ def test_pull_non_empty_request_with_auto_populated_field(): ) +def test_pull_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.pull in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.pull] = mock_rpc + request = {} + client.pull(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.pull(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_pull_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -3577,6 +4214,44 @@ async def test_pull_empty_call_async(): assert args[0] == pubsub.PullRequest() +@pytest.mark.asyncio +async def test_pull_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio"): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.pull in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.pull + ] = mock_object + + request = {} + await client.pull(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.pull(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_pull_async( transport: str = "grpc_asyncio", request_type=pubsub.PullRequest @@ -3808,6 +4483,82 @@ def test_streaming_pull(request_type, transport: str = "grpc"): assert isinstance(message, pubsub.StreamingPullResponse) +def test_streaming_pull_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.streaming_pull in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.streaming_pull] = mock_rpc + request = [{}] + client.streaming_pull(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.streaming_pull(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + +@pytest.mark.asyncio +async def test_streaming_pull_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.streaming_pull + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.streaming_pull + ] = mock_object + + request = [{}] + await client.streaming_pull(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.streaming_pull(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_streaming_pull_async( transport: str = "grpc_asyncio", request_type=pubsub.StreamingPullRequest @@ -3893,6 +4644,9 @@ def test_modify_push_config_empty_call(): with mock.patch.object( type(client.transport.modify_push_config), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.modify_push_config() call.assert_called() _, args, _ = call.mock_calls[0] @@ -3918,6 +4672,9 @@ def test_modify_push_config_non_empty_request_with_auto_populated_field(): with mock.patch.object( type(client.transport.modify_push_config), "__call__" ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.modify_push_config(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -3926,6 +4683,45 @@ def test_modify_push_config_non_empty_request_with_auto_populated_field(): ) +def test_modify_push_config_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.modify_push_config in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.modify_push_config + ] = mock_rpc + request = {} + client.modify_push_config(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.modify_push_config(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_modify_push_config_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -3947,6 +4743,47 @@ async def test_modify_push_config_empty_call_async(): assert args[0] == pubsub.ModifyPushConfigRequest() +@pytest.mark.asyncio +async def test_modify_push_config_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.modify_push_config + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.modify_push_config + ] = mock_object + + request = {} + await client.modify_push_config(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.modify_push_config(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_modify_push_config_async( transport: str = "grpc_asyncio", request_type=pubsub.ModifyPushConfigRequest @@ -4188,6 +5025,9 @@ def test_get_snapshot_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.get_snapshot), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.get_snapshot() call.assert_called() _, args, _ = call.mock_calls[0] @@ -4211,6 +5051,9 @@ def test_get_snapshot_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.get_snapshot), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.get_snapshot(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -4219,6 +5062,41 @@ def test_get_snapshot_non_empty_request_with_auto_populated_field(): ) +def test_get_snapshot_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.get_snapshot in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.get_snapshot] = mock_rpc + request = {} + client.get_snapshot(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.get_snapshot(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_get_snapshot_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -4243,6 +5121,47 @@ async def test_get_snapshot_empty_call_async(): assert args[0] == pubsub.GetSnapshotRequest() +@pytest.mark.asyncio +async def test_get_snapshot_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.get_snapshot + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.get_snapshot + ] = mock_object + + request = {} + await client.get_snapshot(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.get_snapshot(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_get_snapshot_async( transport: str = "grpc_asyncio", request_type=pubsub.GetSnapshotRequest @@ -4469,6 +5388,9 @@ def test_list_snapshots_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.list_snapshots), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.list_snapshots() call.assert_called() _, args, _ = call.mock_calls[0] @@ -4493,6 +5415,9 @@ def test_list_snapshots_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.list_snapshots), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.list_snapshots(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -4502,6 +5427,41 @@ def test_list_snapshots_non_empty_request_with_auto_populated_field(): ) +def test_list_snapshots_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.list_snapshots in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.list_snapshots] = mock_rpc + request = {} + client.list_snapshots(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.list_snapshots(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_list_snapshots_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -4525,6 +5485,47 @@ async def test_list_snapshots_empty_call_async(): assert args[0] == pubsub.ListSnapshotsRequest() +@pytest.mark.asyncio +async def test_list_snapshots_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.list_snapshots + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.list_snapshots + ] = mock_object + + request = {} + await client.list_snapshots(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.list_snapshots(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_list_snapshots_async( transport: str = "grpc_asyncio", request_type=pubsub.ListSnapshotsRequest @@ -4744,13 +5745,13 @@ def test_list_snapshots_pager(transport_name: str = "grpc"): RuntimeError, ) - metadata = () - metadata = tuple(metadata) + ( + expected_metadata = () + expected_metadata = tuple(expected_metadata) + ( gapic_v1.routing_header.to_grpc_metadata((("project", ""),)), ) pager = client.list_snapshots(request={}) - assert pager._metadata == metadata + assert pager._metadata == expected_metadata results = list(pager) assert len(results) == 6 @@ -4945,6 +5946,9 @@ def test_create_snapshot_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.create_snapshot), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.create_snapshot() call.assert_called() _, args, _ = call.mock_calls[0] @@ -4969,6 +5973,9 @@ def test_create_snapshot_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.create_snapshot), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.create_snapshot(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -4978,6 +5985,41 @@ def test_create_snapshot_non_empty_request_with_auto_populated_field(): ) +def test_create_snapshot_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.create_snapshot in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.create_snapshot] = mock_rpc + request = {} + client.create_snapshot(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.create_snapshot(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_create_snapshot_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -5002,6 +6044,47 @@ async def test_create_snapshot_empty_call_async(): assert args[0] == pubsub.CreateSnapshotRequest() +@pytest.mark.asyncio +async def test_create_snapshot_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.create_snapshot + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.create_snapshot + ] = mock_object + + request = {} + await client.create_snapshot(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.create_snapshot(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_create_snapshot_async( transport: str = "grpc_asyncio", request_type=pubsub.CreateSnapshotRequest @@ -5240,6 +6323,9 @@ def test_update_snapshot_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.update_snapshot), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.update_snapshot() call.assert_called() _, args, _ = call.mock_calls[0] @@ -5261,12 +6347,50 @@ def test_update_snapshot_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.update_snapshot), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.update_snapshot(request=request) call.assert_called() _, args, _ = call.mock_calls[0] assert args[0] == pubsub.UpdateSnapshotRequest() +def test_update_snapshot_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.update_snapshot in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.update_snapshot] = mock_rpc + request = {} + client.update_snapshot(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.update_snapshot(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_update_snapshot_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -5291,6 +6415,47 @@ async def test_update_snapshot_empty_call_async(): assert args[0] == pubsub.UpdateSnapshotRequest() +@pytest.mark.asyncio +async def test_update_snapshot_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.update_snapshot + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.update_snapshot + ] = mock_object + + request = {} + await client.update_snapshot(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.update_snapshot(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_update_snapshot_async( transport: str = "grpc_asyncio", request_type=pubsub.UpdateSnapshotRequest @@ -5524,6 +6689,9 @@ def test_delete_snapshot_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.delete_snapshot), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.delete_snapshot() call.assert_called() _, args, _ = call.mock_calls[0] @@ -5547,6 +6715,9 @@ def test_delete_snapshot_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.delete_snapshot), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.delete_snapshot(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -5555,6 +6726,41 @@ def test_delete_snapshot_non_empty_request_with_auto_populated_field(): ) +def test_delete_snapshot_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.delete_snapshot in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.delete_snapshot] = mock_rpc + request = {} + client.delete_snapshot(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.delete_snapshot(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_delete_snapshot_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -5574,6 +6780,47 @@ async def test_delete_snapshot_empty_call_async(): assert args[0] == pubsub.DeleteSnapshotRequest() +@pytest.mark.asyncio +async def test_delete_snapshot_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.delete_snapshot + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.delete_snapshot + ] = mock_object + + request = {} + await client.delete_snapshot(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.delete_snapshot(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_delete_snapshot_async( transport: str = "grpc_asyncio", request_type=pubsub.DeleteSnapshotRequest @@ -5790,6 +7037,9 @@ def test_seek_empty_call(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.seek), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.seek() call.assert_called() _, args, _ = call.mock_calls[0] @@ -5814,6 +7064,9 @@ def test_seek_non_empty_request_with_auto_populated_field(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.seek), "__call__") as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) client.seek(request=request) call.assert_called() _, args, _ = call.mock_calls[0] @@ -5823,6 +7076,41 @@ def test_seek_non_empty_request_with_auto_populated_field(): ) +def test_seek_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.seek in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.seek] = mock_rpc + request = {} + client.seek(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.seek(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + @pytest.mark.asyncio async def test_seek_empty_call_async(): # This test is a coverage failsafe to make sure that totally empty calls, @@ -5842,6 +7130,44 @@ async def test_seek_empty_call_async(): assert args[0] == pubsub.SeekRequest() +@pytest.mark.asyncio +async def test_seek_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio"): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.seek in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_object = mock.AsyncMock() + client._client._transport._wrapped_methods[ + client._client._transport.seek + ] = mock_object + + request = {} + await client.seek(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_object.call_count == 1 + + await client.seek(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_object.call_count == 2 + + @pytest.mark.asyncio async def test_seek_async( transport: str = "grpc_asyncio", request_type=pubsub.SeekRequest @@ -5991,6 +7317,46 @@ def test_create_subscription_rest(request_type): assert response.state == pubsub.Subscription.State.ACTIVE +def test_create_subscription_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.create_subscription in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.create_subscription + ] = mock_rpc + + request = {} + client.create_subscription(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.create_subscription(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_create_subscription_rest_required_fields(request_type=pubsub.Subscription): transport_class = transports.SubscriberRestTransport @@ -6285,6 +7651,44 @@ def test_get_subscription_rest(request_type): assert response.state == pubsub.Subscription.State.ACTIVE +def test_get_subscription_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.get_subscription in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.get_subscription + ] = mock_rpc + + request = {} + client.get_subscription(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.get_subscription(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_get_subscription_rest_required_fields( request_type=pubsub.GetSubscriptionRequest, ): @@ -6563,6 +7967,46 @@ def test_update_subscription_rest(request_type): assert response.state == pubsub.Subscription.State.ACTIVE +def test_update_subscription_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.update_subscription in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.update_subscription + ] = mock_rpc + + request = {} + client.update_subscription(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.update_subscription(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_update_subscription_rest_required_fields( request_type=pubsub.UpdateSubscriptionRequest, ): @@ -6836,6 +8280,46 @@ def test_list_subscriptions_rest(request_type): assert response.next_page_token == "next_page_token_value" +def test_list_subscriptions_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.list_subscriptions in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.list_subscriptions + ] = mock_rpc + + request = {} + client.list_subscriptions(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.list_subscriptions(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_list_subscriptions_rest_required_fields( request_type=pubsub.ListSubscriptionsRequest, ): @@ -7166,6 +8650,46 @@ def test_delete_subscription_rest(request_type): assert response is None +def test_delete_subscription_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.delete_subscription in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.delete_subscription + ] = mock_rpc + + request = {} + client.delete_subscription(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.delete_subscription(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_delete_subscription_rest_required_fields( request_type=pubsub.DeleteSubscriptionRequest, ): @@ -7414,6 +8938,46 @@ def test_modify_ack_deadline_rest(request_type): assert response is None +def test_modify_ack_deadline_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.modify_ack_deadline in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.modify_ack_deadline + ] = mock_rpc + + request = {} + client.modify_ack_deadline(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.modify_ack_deadline(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_modify_ack_deadline_rest_required_fields( request_type=pubsub.ModifyAckDeadlineRequest, ): @@ -7685,6 +9249,42 @@ def test_acknowledge_rest(request_type): assert response is None +def test_acknowledge_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.acknowledge in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.acknowledge] = mock_rpc + + request = {} + client.acknowledge(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.acknowledge(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_acknowledge_rest_required_fields(request_type=pubsub.AcknowledgeRequest): transport_class = transports.SubscriberRestTransport @@ -7947,6 +9547,42 @@ def test_pull_rest(request_type): assert isinstance(response, pubsub.PullResponse) +def test_pull_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.pull in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.pull] = mock_rpc + + request = {} + client.pull(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.pull(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_pull_rest_required_fields(request_type=pubsub.PullRequest): transport_class = transports.SubscriberRestTransport @@ -8231,6 +9867,46 @@ def test_modify_push_config_rest(request_type): assert response is None +def test_modify_push_config_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.modify_push_config in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.modify_push_config + ] = mock_rpc + + request = {} + client.modify_push_config(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.modify_push_config(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_modify_push_config_rest_required_fields( request_type=pubsub.ModifyPushConfigRequest, ): @@ -8496,6 +10172,42 @@ def test_get_snapshot_rest(request_type): assert response.topic == "topic_value" +def test_get_snapshot_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.get_snapshot in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.get_snapshot] = mock_rpc + + request = {} + client.get_snapshot(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.get_snapshot(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_get_snapshot_rest_required_fields(request_type=pubsub.GetSnapshotRequest): transport_class = transports.SubscriberRestTransport @@ -8755,6 +10467,42 @@ def test_list_snapshots_rest(request_type): assert response.next_page_token == "next_page_token_value" +def test_list_snapshots_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.list_snapshots in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.list_snapshots] = mock_rpc + + request = {} + client.list_snapshots(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.list_snapshots(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_list_snapshots_rest_required_fields(request_type=pubsub.ListSnapshotsRequest): transport_class = transports.SubscriberRestTransport @@ -9088,6 +10836,42 @@ def test_create_snapshot_rest(request_type): assert response.topic == "topic_value" +def test_create_snapshot_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.create_snapshot in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.create_snapshot] = mock_rpc + + request = {} + client.create_snapshot(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.create_snapshot(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_create_snapshot_rest_required_fields( request_type=pubsub.CreateSnapshotRequest, ): @@ -9366,6 +11150,42 @@ def test_update_snapshot_rest(request_type): assert response.topic == "topic_value" +def test_update_snapshot_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.update_snapshot in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.update_snapshot] = mock_rpc + + request = {} + client.update_snapshot(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.update_snapshot(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_update_snapshot_rest_required_fields( request_type=pubsub.UpdateSnapshotRequest, ): @@ -9629,6 +11449,42 @@ def test_delete_snapshot_rest(request_type): assert response is None +def test_delete_snapshot_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.delete_snapshot in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.delete_snapshot] = mock_rpc + + request = {} + client.delete_snapshot(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.delete_snapshot(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_delete_snapshot_rest_required_fields( request_type=pubsub.DeleteSnapshotRequest, ): @@ -9876,6 +11732,42 @@ def test_seek_rest(request_type): assert isinstance(response, pubsub.SeekResponse) +def test_seek_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert client._transport.seek in client._transport._wrapped_methods + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.seek] = mock_rpc + + request = {} + client.seek(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.seek(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + def test_seek_rest_required_fields(request_type=pubsub.SeekRequest): transport_class = transports.SubscriberRestTransport diff --git a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py index a09d85b00..16a6150af 100644 --- a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py +++ b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py @@ -299,9 +299,10 @@ async def test_sync_pull_warning_if_return_immediately_async(creds): client = SubscriberAsyncClient(credentials=creds) subscription_path = "projects/foo/subscriptions/bar" - patcher = mock.patch( - "google.pubsub_v1.services.subscriber.async_client.gapic_v1.method_async.wrap_method", - new=mock.AsyncMock, + patcher = mock.patch.object( + type(client.transport.pull), + "__call__", + new_callable=mock.AsyncMock, ) with patcher, pytest.warns( From d9f0f3ac09bebb8ee02df3a047630650fb2fec08 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Sat, 6 Jul 2024 01:35:45 -0400 Subject: [PATCH 081/190] chore(main): release 2.22.0 (#1216) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index cd075e262..b433c0bb9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.21.5" + ".": "2.22.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 96442ed5f..55cb6c9ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.22.0](https://github.com/googleapis/python-pubsub/compare/v2.21.5...v2.22.0) (2024-07-06) + + +### Features + +* Add service_account_email for export subscriptions ([ec0cc34](https://github.com/googleapis/python-pubsub/commit/ec0cc349b344b6882979838171b6cae4209a9b02)) +* Add use_topic_schema for Cloud Storage Subscriptions ([ec0cc34](https://github.com/googleapis/python-pubsub/commit/ec0cc349b344b6882979838171b6cae4209a9b02)) + ## [2.21.5](https://github.com/googleapis/python-pubsub/compare/v2.21.4...v2.21.5) (2024-06-20) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 1f7391fd4..03d6d0200 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.21.5" # {x-release-please-version} +__version__ = "2.22.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 1f7391fd4..03d6d0200 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.21.5" # {x-release-please-version} +__version__ = "2.22.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index d66015ac4..e8d116b9b 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "0.1.0" + "version": "2.22.0" }, "snippets": [ { From ab1ca101d932e1325e24f35a4a619eea2eca93f9 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Sun, 7 Jul 2024 00:58:03 +0200 Subject: [PATCH 082/190] chore(deps): update all dependencies (#1215) Co-authored-by: Owl Bot Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> From 8f9a4148073c0602ff025e7f4fa17b0234876c26 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Sun, 7 Jul 2024 01:51:17 +0200 Subject: [PATCH 083/190] chore(deps): update all dependencies (#1217) --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 9955737c3..0f8d2f938 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,4 +1,4 @@ -google-cloud-pubsub==2.21.5 +google-cloud-pubsub==2.22.0 avro==1.11.3 protobuf===4.24.4; python_version == '3.7' protobuf==5.27.2; python_version >= '3.8' From 3c8f8f923f531e0df2a917ced206f511218c0f0e Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 16:12:30 -0400 Subject: [PATCH 084/190] chore(python): update dependencies in .kokoro (#1220) Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- .github/.OwlBot.lock.yaml | 4 +-- .kokoro/docker/docs/Dockerfile | 22 +++++++------ .kokoro/docker/docs/requirements.txt | 40 +++++++++++++----------- .kokoro/requirements.txt | 46 ++++++++++++++-------------- noxfile.py | 2 +- 5 files changed, 61 insertions(+), 53 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 76524393f..f9451fda6 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:5651442a6336971a2fb2df40fb56b3337df67cafa14c0809cc89cb34ccee1b8e -# created: 2024-07-04T19:38:10.086106449Z + digest: sha256:99ab465187b4891e878ee4f9977b4a6aeeb0ceadf404870c416c50e06500eb42 +# created: 2024-07-08T16:17:14.833595692Z diff --git a/.kokoro/docker/docs/Dockerfile b/.kokoro/docker/docs/Dockerfile index a26ce6193..741084af5 100644 --- a/.kokoro/docker/docs/Dockerfile +++ b/.kokoro/docker/docs/Dockerfile @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ubuntu:22.04 +from ubuntu:24.04 ENV DEBIAN_FRONTEND noninteractive @@ -40,7 +40,6 @@ RUN apt-get update \ libssl-dev \ libsqlite3-dev \ portaudio19-dev \ - python3-distutils \ redis-server \ software-properties-common \ ssh \ @@ -60,18 +59,23 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* \ && rm -f /var/cache/apt/archives/*.deb -###################### Install python 3.9.13 +###################### Install python 3.10.14 for docfx session -# Download python 3.9.13 -RUN wget https://www.python.org/ftp/python/3.9.13/Python-3.9.13.tgz +# Download python 3.10.14 +RUN wget https://www.python.org/ftp/python/3.10.14/Python-3.10.14.tgz # Extract files -RUN tar -xvf Python-3.9.13.tgz +RUN tar -xvf Python-3.10.14.tgz -# Install python 3.9.13 -RUN ./Python-3.9.13/configure --enable-optimizations +# Install python 3.10.14 +RUN ./Python-3.10.14/configure --enable-optimizations RUN make altinstall +###################### Use python 3.10 by default + +RUN python3.10 -m venv /venv +ENV PATH /venv/bin:$PATH + ###################### Install pip RUN wget -O /tmp/get-pip.py 'https://bootstrap.pypa.io/get-pip.py' \ && python3 /tmp/get-pip.py \ @@ -84,4 +88,4 @@ RUN python3 -m pip COPY requirements.txt /requirements.txt RUN python3 -m pip install --require-hashes -r requirements.txt -CMD ["python3.8"] +CMD ["python3.10"] diff --git a/.kokoro/docker/docs/requirements.txt b/.kokoro/docker/docs/requirements.txt index 0e5d70f20..7129c7715 100644 --- a/.kokoro/docker/docs/requirements.txt +++ b/.kokoro/docker/docs/requirements.txt @@ -4,9 +4,9 @@ # # pip-compile --allow-unsafe --generate-hashes requirements.in # -argcomplete==3.2.3 \ - --hash=sha256:bf7900329262e481be5a15f56f19736b376df6f82ed27576fa893652c5de6c23 \ - --hash=sha256:c12355e0494c76a2a7b73e3a59b09024ca0ba1e279fb9ed6c1b82d5b74b6a70c +argcomplete==3.4.0 \ + --hash=sha256:69a79e083a716173e5532e0fa3bef45f793f4e61096cf52b5a42c0211c8b8aa5 \ + --hash=sha256:c2abcdfe1be8ace47ba777d4fce319eb13bf8ad9dace8d085dcad6eded88057f # via nox colorlog==6.8.2 \ --hash=sha256:3e3e079a41feb5a1b64f978b5ea4f46040a94f11f0e8bbb8261e3dbbeca64d44 \ @@ -16,23 +16,27 @@ distlib==0.3.8 \ --hash=sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784 \ --hash=sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64 # via virtualenv -filelock==3.13.1 \ - --hash=sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e \ - --hash=sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c +filelock==3.15.4 \ + --hash=sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb \ + --hash=sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7 # via virtualenv -nox==2024.3.2 \ - --hash=sha256:e53514173ac0b98dd47585096a55572fe504fecede58ced708979184d05440be \ - --hash=sha256:f521ae08a15adbf5e11f16cb34e8d0e6ea521e0b92868f684e91677deb974553 +nox==2024.4.15 \ + --hash=sha256:6492236efa15a460ecb98e7b67562a28b70da006ab0be164e8821177577c0565 \ + --hash=sha256:ecf6700199cdfa9e5ea0a41ff5e6ef4641d09508eda6edb89d9987864115817f # via -r requirements.in -packaging==24.0 \ - --hash=sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5 \ - --hash=sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9 +packaging==24.1 \ + --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ + --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 # via nox -platformdirs==4.2.0 \ - --hash=sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068 \ - --hash=sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768 +platformdirs==4.2.2 \ + --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \ + --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3 # via virtualenv -virtualenv==20.25.1 \ - --hash=sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a \ - --hash=sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197 +tomli==2.0.1 \ + --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ + --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f + # via nox +virtualenv==20.26.3 \ + --hash=sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a \ + --hash=sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589 # via nox diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 35ece0e4d..9622baf0b 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -20,9 +20,9 @@ cachetools==5.3.3 \ --hash=sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945 \ --hash=sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105 # via google-auth -certifi==2024.6.2 \ - --hash=sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516 \ - --hash=sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56 +certifi==2024.7.4 \ + --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \ + --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90 # via requests cffi==1.16.0 \ --hash=sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc \ @@ -371,23 +371,23 @@ more-itertools==10.3.0 \ # via # jaraco-classes # jaraco-functools -nh3==0.2.17 \ - --hash=sha256:0316c25b76289cf23be6b66c77d3608a4fdf537b35426280032f432f14291b9a \ - --hash=sha256:1a814dd7bba1cb0aba5bcb9bebcc88fd801b63e21e2450ae6c52d3b3336bc911 \ - --hash=sha256:1aa52a7def528297f256de0844e8dd680ee279e79583c76d6fa73a978186ddfb \ - --hash=sha256:22c26e20acbb253a5bdd33d432a326d18508a910e4dcf9a3316179860d53345a \ - --hash=sha256:40015514022af31975c0b3bca4014634fa13cb5dc4dbcbc00570acc781316dcc \ - --hash=sha256:40d0741a19c3d645e54efba71cb0d8c475b59135c1e3c580f879ad5514cbf028 \ - --hash=sha256:551672fd71d06cd828e282abdb810d1be24e1abb7ae2543a8fa36a71c1006fe9 \ - --hash=sha256:66f17d78826096291bd264f260213d2b3905e3c7fae6dfc5337d49429f1dc9f3 \ - --hash=sha256:85cdbcca8ef10733bd31f931956f7fbb85145a4d11ab9e6742bbf44d88b7e351 \ - --hash=sha256:a3f55fabe29164ba6026b5ad5c3151c314d136fd67415a17660b4aaddacf1b10 \ - --hash=sha256:b4427ef0d2dfdec10b641ed0bdaf17957eb625b2ec0ea9329b3d28806c153d71 \ - --hash=sha256:ba73a2f8d3a1b966e9cdba7b211779ad8a2561d2dba9674b8a19ed817923f65f \ - --hash=sha256:c21bac1a7245cbd88c0b0e4a420221b7bfa838a2814ee5bb924e9c2f10a1120b \ - --hash=sha256:c551eb2a3876e8ff2ac63dff1585236ed5dfec5ffd82216a7a174f7c5082a78a \ - --hash=sha256:c790769152308421283679a142dbdb3d1c46c79c823008ecea8e8141db1a2062 \ - --hash=sha256:d7a25fd8c86657f5d9d576268e3b3767c5cd4f42867c9383618be8517f0f022a +nh3==0.2.18 \ + --hash=sha256:0411beb0589eacb6734f28d5497ca2ed379eafab8ad8c84b31bb5c34072b7164 \ + --hash=sha256:14c5a72e9fe82aea5fe3072116ad4661af5cf8e8ff8fc5ad3450f123e4925e86 \ + --hash=sha256:19aaba96e0f795bd0a6c56291495ff59364f4300d4a39b29a0abc9cb3774a84b \ + --hash=sha256:34c03fa78e328c691f982b7c03d4423bdfd7da69cd707fe572f544cf74ac23ad \ + --hash=sha256:36c95d4b70530b320b365659bb5034341316e6a9b30f0b25fa9c9eff4c27a204 \ + --hash=sha256:3a157ab149e591bb638a55c8c6bcb8cdb559c8b12c13a8affaba6cedfe51713a \ + --hash=sha256:42c64511469005058cd17cc1537578eac40ae9f7200bedcfd1fc1a05f4f8c200 \ + --hash=sha256:5f36b271dae35c465ef5e9090e1fdaba4a60a56f0bb0ba03e0932a66f28b9189 \ + --hash=sha256:6955369e4d9f48f41e3f238a9e60f9410645db7e07435e62c6a9ea6135a4907f \ + --hash=sha256:7b7c2a3c9eb1a827d42539aa64091640bd275b81e097cd1d8d82ef91ffa2e811 \ + --hash=sha256:8ce0f819d2f1933953fca255db2471ad58184a60508f03e6285e5114b6254844 \ + --hash=sha256:94a166927e53972a9698af9542ace4e38b9de50c34352b962f4d9a7d4c927af4 \ + --hash=sha256:a7f1b5b2c15866f2db413a3649a8fe4fd7b428ae58be2c0f6bca5eefd53ca2be \ + --hash=sha256:c8b3a1cebcba9b3669ed1a84cc65bf005728d2f0bc1ed2a6594a992e817f3a50 \ + --hash=sha256:de3ceed6e661954871d6cd78b410213bdcb136f79aafe22aa7182e028b8c7307 \ + --hash=sha256:f0eca9ca8628dbb4e916ae2491d72957fdd35f7a5d326b7032a345f111ac07fe # via readme-renderer nox==2024.4.15 \ --hash=sha256:6492236efa15a460ecb98e7b67562a28b70da006ab0be164e8821177577c0565 \ @@ -460,9 +460,9 @@ python-dateutil==2.9.0.post0 \ --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 # via gcp-releasetool -readme-renderer==43.0 \ - --hash=sha256:1818dd28140813509eeed8d62687f7cd4f7bad90d4db586001c5dc09d4fde311 \ - --hash=sha256:19db308d86ecd60e5affa3b2a98f017af384678c63c88e5d4556a380e674f3f9 +readme-renderer==44.0 \ + --hash=sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151 \ + --hash=sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1 # via twine requests==2.32.3 \ --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \ diff --git a/noxfile.py b/noxfile.py index 1d9b72ca7..c6d0c11a3 100644 --- a/noxfile.py +++ b/noxfile.py @@ -341,7 +341,7 @@ def cover(session): session.run("coverage", "erase") -@nox.session(python="3.9") +@nox.session(python="3.10") def docs(session): """Build the docs for this library.""" From fbd98402077f63bd839ffd23875e88ef3dab3d99 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Mon, 8 Jul 2024 14:06:53 -0700 Subject: [PATCH 085/190] chore(python): use python 3.10 for docs build (#1221) Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 ++-- .kokoro/docker/docs/Dockerfile | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index f9451fda6..f30cb3775 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:99ab465187b4891e878ee4f9977b4a6aeeb0ceadf404870c416c50e06500eb42 -# created: 2024-07-08T16:17:14.833595692Z + digest: sha256:52210e0e0559f5ea8c52be148b33504022e1faef4e95fbe4b32d68022af2fa7e +# created: 2024-07-08T19:25:35.862283192Z diff --git a/.kokoro/docker/docs/Dockerfile b/.kokoro/docker/docs/Dockerfile index 741084af5..5205308b3 100644 --- a/.kokoro/docker/docs/Dockerfile +++ b/.kokoro/docker/docs/Dockerfile @@ -59,7 +59,8 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* \ && rm -f /var/cache/apt/archives/*.deb -###################### Install python 3.10.14 for docfx session + +###################### Install python 3.10.14 for docs/docfx session # Download python 3.10.14 RUN wget https://www.python.org/ftp/python/3.10.14/Python-3.10.14.tgz @@ -71,8 +72,6 @@ RUN tar -xvf Python-3.10.14.tgz RUN ./Python-3.10.14/configure --enable-optimizations RUN make altinstall -###################### Use python 3.10 by default - RUN python3.10 -m venv /venv ENV PATH /venv/bin:$PATH From 91c89d36c5099591408ab0661c55929e786b1b04 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 11:58:29 -0400 Subject: [PATCH 086/190] feat: add max messages batching for Cloud Storage subscriptions (#1224) Co-authored-by: Owl Bot --- google/pubsub_v1/types/pubsub.py | 8 ++++++++ .../snippet_metadata_google.pubsub.v1.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index a45a34a14..5f8266014 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -1432,6 +1432,10 @@ class CloudStorageConfig(proto.Message): Storage file before a new file is created. Min 1 KB, max 10 GiB. The max_bytes limit may be exceeded in cases where messages are larger than the limit. + max_messages (int): + Optional. The maximum number of messages that + can be written to a Cloud Storage file before a + new file is created. Min 1000 messages. state (google.pubsub_v1.types.CloudStorageConfig.State): Output only. An output-only field that indicates whether or not the subscription can @@ -1551,6 +1555,10 @@ class AvroConfig(proto.Message): proto.INT64, number=7, ) + max_messages: int = proto.Field( + proto.INT64, + number=8, + ) state: State = proto.Field( proto.ENUM, number=9, diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index e8d116b9b..d66015ac4 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.22.0" + "version": "0.1.0" }, "snippets": [ { From 5aca74891ad6cd33891617dcc71ce98e3f98eccd Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 09:54:04 -0700 Subject: [PATCH 087/190] chore(main): release 2.23.0 (#1230) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b433c0bb9..b6be5b944 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.22.0" + ".": "2.23.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 55cb6c9ec..a410d0dc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.23.0](https://github.com/googleapis/python-pubsub/compare/v2.22.0...v2.23.0) (2024-07-29) + + +### Features + +* Add max messages batching for Cloud Storage subscriptions ([#1224](https://github.com/googleapis/python-pubsub/issues/1224)) ([91c89d3](https://github.com/googleapis/python-pubsub/commit/91c89d36c5099591408ab0661c55929e786b1b04)) + ## [2.22.0](https://github.com/googleapis/python-pubsub/compare/v2.21.5...v2.22.0) (2024-07-06) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 03d6d0200..f01e1d3a5 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.22.0" # {x-release-please-version} +__version__ = "2.23.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 03d6d0200..f01e1d3a5 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.22.0" # {x-release-please-version} +__version__ = "2.23.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index d66015ac4..d391b168b 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "0.1.0" + "version": "2.23.0" }, "snippets": [ { From 7cf2d11ce20bf9080c4434b920bdf419faac5d88 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 09:38:17 -0700 Subject: [PATCH 088/190] chore: Update gapic-generator-python to v1.18.5 (#1231) Co-authored-by: Owl Bot --- .../services/publisher/async_client.py | 11 +- google/pubsub_v1/services/publisher/client.py | 8 +- google/pubsub_v1/services/publisher/pagers.py | 97 ++++++++++- .../services/schema_service/async_client.py | 9 +- .../services/schema_service/client.py | 6 +- .../services/schema_service/pagers.py | 69 +++++++- .../services/subscriber/async_client.py | 9 +- .../pubsub_v1/services/subscriber/client.py | 6 +- .../pubsub_v1/services/subscriber/pagers.py | 69 +++++++- .../snippet_metadata_google.pubsub.v1.json | 2 +- tests/unit/gapic/pubsub_v1/test_publisher.py | 102 +++++++----- .../gapic/pubsub_v1/test_schema_service.py | 103 +++++++----- tests/unit/gapic/pubsub_v1/test_subscriber.py | 157 ++++++++++-------- 13 files changed, 471 insertions(+), 177 deletions(-) diff --git a/google/pubsub_v1/services/publisher/async_client.py b/google/pubsub_v1/services/publisher/async_client.py index 2b6df487d..eda259ad5 100644 --- a/google/pubsub_v1/services/publisher/async_client.py +++ b/google/pubsub_v1/services/publisher/async_client.py @@ -14,7 +14,6 @@ # limitations under the License. # from collections import OrderedDict -import functools import re from typing import ( Dict, @@ -192,9 +191,7 @@ def universe_domain(self) -> str: """ return self._client._universe_domain - get_transport_class = functools.partial( - type(PublisherClient).get_transport_class, type(PublisherClient) - ) + get_transport_class = PublisherClient.get_transport_class def __init__( self, @@ -826,6 +823,8 @@ async def sample_list_topics(): method=rpc, request=request, response=response, + retry=retry, + timeout=timeout, metadata=metadata, ) @@ -946,6 +945,8 @@ async def sample_list_topic_subscriptions(): method=rpc, request=request, response=response, + retry=retry, + timeout=timeout, metadata=metadata, ) @@ -1070,6 +1071,8 @@ async def sample_list_topic_snapshots(): method=rpc, request=request, response=response, + retry=retry, + timeout=timeout, metadata=metadata, ) diff --git a/google/pubsub_v1/services/publisher/client.py b/google/pubsub_v1/services/publisher/client.py index f07c4bbd4..5a4b5a6ff 100644 --- a/google/pubsub_v1/services/publisher/client.py +++ b/google/pubsub_v1/services/publisher/client.py @@ -707,7 +707,7 @@ def __init__( transport_init: Union[ Type[PublisherTransport], Callable[..., PublisherTransport] ] = ( - type(self).get_transport_class(transport) + PublisherClient.get_transport_class(transport) if isinstance(transport, str) or transport is None else cast(Callable[..., PublisherTransport], transport) ) @@ -1284,6 +1284,8 @@ def sample_list_topics(): method=rpc, request=request, response=response, + retry=retry, + timeout=timeout, metadata=metadata, ) @@ -1401,6 +1403,8 @@ def sample_list_topic_subscriptions(): method=rpc, request=request, response=response, + retry=retry, + timeout=timeout, metadata=metadata, ) @@ -1522,6 +1526,8 @@ def sample_list_topic_snapshots(): method=rpc, request=request, response=response, + retry=retry, + timeout=timeout, metadata=metadata, ) diff --git a/google/pubsub_v1/services/publisher/pagers.py b/google/pubsub_v1/services/publisher/pagers.py index 21ae22b8a..de3490c39 100644 --- a/google/pubsub_v1/services/publisher/pagers.py +++ b/google/pubsub_v1/services/publisher/pagers.py @@ -13,6 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # +from google.api_core import gapic_v1 +from google.api_core import retry as retries +from google.api_core import retry_async as retries_async from typing import ( Any, AsyncIterator, @@ -22,8 +25,18 @@ Tuple, Optional, Iterator, + Union, ) +try: + OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] + OptionalAsyncRetry = Union[ + retries_async.AsyncRetry, gapic_v1.method._MethodDefault, None + ] +except AttributeError: # pragma: NO COVER + OptionalRetry = Union[retries.Retry, object, None] # type: ignore + OptionalAsyncRetry = Union[retries_async.AsyncRetry, object, None] # type: ignore + from google.pubsub_v1.types import pubsub @@ -51,6 +64,8 @@ def __init__( request: pubsub.ListTopicsRequest, response: pubsub.ListTopicsResponse, *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = () ): """Instantiate the pager. @@ -62,12 +77,17 @@ def __init__( The initial request object. response (google.pubsub_v1.types.ListTopicsResponse): The initial response object. + retry (google.api_core.retry.Retry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. """ self._method = method self._request = pubsub.ListTopicsRequest(request) self._response = response + self._retry = retry + self._timeout = timeout self._metadata = metadata def __getattr__(self, name: str) -> Any: @@ -78,7 +98,12 @@ def pages(self) -> Iterator[pubsub.ListTopicsResponse]: yield self._response while self._response.next_page_token: self._request.page_token = self._response.next_page_token - self._response = self._method(self._request, metadata=self._metadata) + self._response = self._method( + self._request, + retry=self._retry, + timeout=self._timeout, + metadata=self._metadata, + ) yield self._response def __iter__(self) -> Iterator[pubsub.Topic]: @@ -113,6 +138,8 @@ def __init__( request: pubsub.ListTopicsRequest, response: pubsub.ListTopicsResponse, *, + retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = () ): """Instantiates the pager. @@ -124,12 +151,17 @@ def __init__( The initial request object. response (google.pubsub_v1.types.ListTopicsResponse): The initial response object. + retry (google.api_core.retry.AsyncRetry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. """ self._method = method self._request = pubsub.ListTopicsRequest(request) self._response = response + self._retry = retry + self._timeout = timeout self._metadata = metadata def __getattr__(self, name: str) -> Any: @@ -140,7 +172,12 @@ async def pages(self) -> AsyncIterator[pubsub.ListTopicsResponse]: yield self._response while self._response.next_page_token: self._request.page_token = self._response.next_page_token - self._response = await self._method(self._request, metadata=self._metadata) + self._response = await self._method( + self._request, + retry=self._retry, + timeout=self._timeout, + metadata=self._metadata, + ) yield self._response def __aiter__(self) -> AsyncIterator[pubsub.Topic]: @@ -179,6 +216,8 @@ def __init__( request: pubsub.ListTopicSubscriptionsRequest, response: pubsub.ListTopicSubscriptionsResponse, *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = () ): """Instantiate the pager. @@ -190,12 +229,17 @@ def __init__( The initial request object. response (google.pubsub_v1.types.ListTopicSubscriptionsResponse): The initial response object. + retry (google.api_core.retry.Retry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. """ self._method = method self._request = pubsub.ListTopicSubscriptionsRequest(request) self._response = response + self._retry = retry + self._timeout = timeout self._metadata = metadata def __getattr__(self, name: str) -> Any: @@ -206,7 +250,12 @@ def pages(self) -> Iterator[pubsub.ListTopicSubscriptionsResponse]: yield self._response while self._response.next_page_token: self._request.page_token = self._response.next_page_token - self._response = self._method(self._request, metadata=self._metadata) + self._response = self._method( + self._request, + retry=self._retry, + timeout=self._timeout, + metadata=self._metadata, + ) yield self._response def __iter__(self) -> Iterator[str]: @@ -241,6 +290,8 @@ def __init__( request: pubsub.ListTopicSubscriptionsRequest, response: pubsub.ListTopicSubscriptionsResponse, *, + retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = () ): """Instantiates the pager. @@ -252,12 +303,17 @@ def __init__( The initial request object. response (google.pubsub_v1.types.ListTopicSubscriptionsResponse): The initial response object. + retry (google.api_core.retry.AsyncRetry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. """ self._method = method self._request = pubsub.ListTopicSubscriptionsRequest(request) self._response = response + self._retry = retry + self._timeout = timeout self._metadata = metadata def __getattr__(self, name: str) -> Any: @@ -268,7 +324,12 @@ async def pages(self) -> AsyncIterator[pubsub.ListTopicSubscriptionsResponse]: yield self._response while self._response.next_page_token: self._request.page_token = self._response.next_page_token - self._response = await self._method(self._request, metadata=self._metadata) + self._response = await self._method( + self._request, + retry=self._retry, + timeout=self._timeout, + metadata=self._metadata, + ) yield self._response def __aiter__(self) -> AsyncIterator[str]: @@ -307,6 +368,8 @@ def __init__( request: pubsub.ListTopicSnapshotsRequest, response: pubsub.ListTopicSnapshotsResponse, *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = () ): """Instantiate the pager. @@ -318,12 +381,17 @@ def __init__( The initial request object. response (google.pubsub_v1.types.ListTopicSnapshotsResponse): The initial response object. + retry (google.api_core.retry.Retry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. """ self._method = method self._request = pubsub.ListTopicSnapshotsRequest(request) self._response = response + self._retry = retry + self._timeout = timeout self._metadata = metadata def __getattr__(self, name: str) -> Any: @@ -334,7 +402,12 @@ def pages(self) -> Iterator[pubsub.ListTopicSnapshotsResponse]: yield self._response while self._response.next_page_token: self._request.page_token = self._response.next_page_token - self._response = self._method(self._request, metadata=self._metadata) + self._response = self._method( + self._request, + retry=self._retry, + timeout=self._timeout, + metadata=self._metadata, + ) yield self._response def __iter__(self) -> Iterator[str]: @@ -369,6 +442,8 @@ def __init__( request: pubsub.ListTopicSnapshotsRequest, response: pubsub.ListTopicSnapshotsResponse, *, + retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = () ): """Instantiates the pager. @@ -380,12 +455,17 @@ def __init__( The initial request object. response (google.pubsub_v1.types.ListTopicSnapshotsResponse): The initial response object. + retry (google.api_core.retry.AsyncRetry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. """ self._method = method self._request = pubsub.ListTopicSnapshotsRequest(request) self._response = response + self._retry = retry + self._timeout = timeout self._metadata = metadata def __getattr__(self, name: str) -> Any: @@ -396,7 +476,12 @@ async def pages(self) -> AsyncIterator[pubsub.ListTopicSnapshotsResponse]: yield self._response while self._response.next_page_token: self._request.page_token = self._response.next_page_token - self._response = await self._method(self._request, metadata=self._metadata) + self._response = await self._method( + self._request, + retry=self._retry, + timeout=self._timeout, + metadata=self._metadata, + ) yield self._response def __aiter__(self) -> AsyncIterator[str]: diff --git a/google/pubsub_v1/services/schema_service/async_client.py b/google/pubsub_v1/services/schema_service/async_client.py index f8d1ac4da..9c8eecaec 100644 --- a/google/pubsub_v1/services/schema_service/async_client.py +++ b/google/pubsub_v1/services/schema_service/async_client.py @@ -14,7 +14,6 @@ # limitations under the License. # from collections import OrderedDict -import functools import re from typing import ( Dict, @@ -191,9 +190,7 @@ def universe_domain(self) -> str: """ return self._client._universe_domain - get_transport_class = functools.partial( - type(SchemaServiceClient).get_transport_class, type(SchemaServiceClient) - ) + get_transport_class = SchemaServiceClient.get_transport_class def __init__( self, @@ -609,6 +606,8 @@ async def sample_list_schemas(): method=rpc, request=request, response=response, + retry=retry, + timeout=timeout, metadata=metadata, ) @@ -726,6 +725,8 @@ async def sample_list_schema_revisions(): method=rpc, request=request, response=response, + retry=retry, + timeout=timeout, metadata=metadata, ) diff --git a/google/pubsub_v1/services/schema_service/client.py b/google/pubsub_v1/services/schema_service/client.py index ca9d4f19f..dff44944d 100644 --- a/google/pubsub_v1/services/schema_service/client.py +++ b/google/pubsub_v1/services/schema_service/client.py @@ -659,7 +659,7 @@ def __init__( transport_init: Union[ Type[SchemaServiceTransport], Callable[..., SchemaServiceTransport] ] = ( - type(self).get_transport_class(transport) + SchemaServiceClient.get_transport_class(transport) if isinstance(transport, str) or transport is None else cast(Callable[..., SchemaServiceTransport], transport) ) @@ -1024,6 +1024,8 @@ def sample_list_schemas(): method=rpc, request=request, response=response, + retry=retry, + timeout=timeout, metadata=metadata, ) @@ -1138,6 +1140,8 @@ def sample_list_schema_revisions(): method=rpc, request=request, response=response, + retry=retry, + timeout=timeout, metadata=metadata, ) diff --git a/google/pubsub_v1/services/schema_service/pagers.py b/google/pubsub_v1/services/schema_service/pagers.py index 309e57f53..fa42a6b8c 100644 --- a/google/pubsub_v1/services/schema_service/pagers.py +++ b/google/pubsub_v1/services/schema_service/pagers.py @@ -13,6 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # +from google.api_core import gapic_v1 +from google.api_core import retry as retries +from google.api_core import retry_async as retries_async from typing import ( Any, AsyncIterator, @@ -22,8 +25,18 @@ Tuple, Optional, Iterator, + Union, ) +try: + OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] + OptionalAsyncRetry = Union[ + retries_async.AsyncRetry, gapic_v1.method._MethodDefault, None + ] +except AttributeError: # pragma: NO COVER + OptionalRetry = Union[retries.Retry, object, None] # type: ignore + OptionalAsyncRetry = Union[retries_async.AsyncRetry, object, None] # type: ignore + from google.pubsub_v1.types import schema @@ -51,6 +64,8 @@ def __init__( request: schema.ListSchemasRequest, response: schema.ListSchemasResponse, *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = () ): """Instantiate the pager. @@ -62,12 +77,17 @@ def __init__( The initial request object. response (google.pubsub_v1.types.ListSchemasResponse): The initial response object. + retry (google.api_core.retry.Retry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. """ self._method = method self._request = schema.ListSchemasRequest(request) self._response = response + self._retry = retry + self._timeout = timeout self._metadata = metadata def __getattr__(self, name: str) -> Any: @@ -78,7 +98,12 @@ def pages(self) -> Iterator[schema.ListSchemasResponse]: yield self._response while self._response.next_page_token: self._request.page_token = self._response.next_page_token - self._response = self._method(self._request, metadata=self._metadata) + self._response = self._method( + self._request, + retry=self._retry, + timeout=self._timeout, + metadata=self._metadata, + ) yield self._response def __iter__(self) -> Iterator[schema.Schema]: @@ -113,6 +138,8 @@ def __init__( request: schema.ListSchemasRequest, response: schema.ListSchemasResponse, *, + retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = () ): """Instantiates the pager. @@ -124,12 +151,17 @@ def __init__( The initial request object. response (google.pubsub_v1.types.ListSchemasResponse): The initial response object. + retry (google.api_core.retry.AsyncRetry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. """ self._method = method self._request = schema.ListSchemasRequest(request) self._response = response + self._retry = retry + self._timeout = timeout self._metadata = metadata def __getattr__(self, name: str) -> Any: @@ -140,7 +172,12 @@ async def pages(self) -> AsyncIterator[schema.ListSchemasResponse]: yield self._response while self._response.next_page_token: self._request.page_token = self._response.next_page_token - self._response = await self._method(self._request, metadata=self._metadata) + self._response = await self._method( + self._request, + retry=self._retry, + timeout=self._timeout, + metadata=self._metadata, + ) yield self._response def __aiter__(self) -> AsyncIterator[schema.Schema]: @@ -179,6 +216,8 @@ def __init__( request: schema.ListSchemaRevisionsRequest, response: schema.ListSchemaRevisionsResponse, *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = () ): """Instantiate the pager. @@ -190,12 +229,17 @@ def __init__( The initial request object. response (google.pubsub_v1.types.ListSchemaRevisionsResponse): The initial response object. + retry (google.api_core.retry.Retry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. """ self._method = method self._request = schema.ListSchemaRevisionsRequest(request) self._response = response + self._retry = retry + self._timeout = timeout self._metadata = metadata def __getattr__(self, name: str) -> Any: @@ -206,7 +250,12 @@ def pages(self) -> Iterator[schema.ListSchemaRevisionsResponse]: yield self._response while self._response.next_page_token: self._request.page_token = self._response.next_page_token - self._response = self._method(self._request, metadata=self._metadata) + self._response = self._method( + self._request, + retry=self._retry, + timeout=self._timeout, + metadata=self._metadata, + ) yield self._response def __iter__(self) -> Iterator[schema.Schema]: @@ -241,6 +290,8 @@ def __init__( request: schema.ListSchemaRevisionsRequest, response: schema.ListSchemaRevisionsResponse, *, + retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = () ): """Instantiates the pager. @@ -252,12 +303,17 @@ def __init__( The initial request object. response (google.pubsub_v1.types.ListSchemaRevisionsResponse): The initial response object. + retry (google.api_core.retry.AsyncRetry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. """ self._method = method self._request = schema.ListSchemaRevisionsRequest(request) self._response = response + self._retry = retry + self._timeout = timeout self._metadata = metadata def __getattr__(self, name: str) -> Any: @@ -268,7 +324,12 @@ async def pages(self) -> AsyncIterator[schema.ListSchemaRevisionsResponse]: yield self._response while self._response.next_page_token: self._request.page_token = self._response.next_page_token - self._response = await self._method(self._request, metadata=self._metadata) + self._response = await self._method( + self._request, + retry=self._retry, + timeout=self._timeout, + metadata=self._metadata, + ) yield self._response def __aiter__(self) -> AsyncIterator[schema.Schema]: diff --git a/google/pubsub_v1/services/subscriber/async_client.py b/google/pubsub_v1/services/subscriber/async_client.py index 1a7295131..dcd4f0bbb 100644 --- a/google/pubsub_v1/services/subscriber/async_client.py +++ b/google/pubsub_v1/services/subscriber/async_client.py @@ -14,7 +14,6 @@ # limitations under the License. # from collections import OrderedDict -import functools import re from typing import ( Dict, @@ -198,9 +197,7 @@ def universe_domain(self) -> str: """ return self._client._universe_domain - get_transport_class = functools.partial( - type(SubscriberClient).get_transport_class, type(SubscriberClient) - ) + get_transport_class = SubscriberClient.get_transport_class def __init__( self, @@ -803,6 +800,8 @@ async def sample_list_subscriptions(): method=rpc, request=request, response=response, + retry=retry, + timeout=timeout, metadata=metadata, ) @@ -1736,6 +1735,8 @@ async def sample_list_snapshots(): method=rpc, request=request, response=response, + retry=retry, + timeout=timeout, metadata=metadata, ) diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index bc7639c59..9bad804c6 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -711,7 +711,7 @@ def __init__( transport_init: Union[ Type[SubscriberTransport], Callable[..., SubscriberTransport] ] = ( - type(self).get_transport_class(transport) + SubscriberClient.get_transport_class(transport) if isinstance(transport, str) or transport is None else cast(Callable[..., SubscriberTransport], transport) ) @@ -1260,6 +1260,8 @@ def sample_list_subscriptions(): method=rpc, request=request, response=response, + retry=retry, + timeout=timeout, metadata=metadata, ) @@ -2177,6 +2179,8 @@ def sample_list_snapshots(): method=rpc, request=request, response=response, + retry=retry, + timeout=timeout, metadata=metadata, ) diff --git a/google/pubsub_v1/services/subscriber/pagers.py b/google/pubsub_v1/services/subscriber/pagers.py index 94c88b9b3..c09c42027 100644 --- a/google/pubsub_v1/services/subscriber/pagers.py +++ b/google/pubsub_v1/services/subscriber/pagers.py @@ -13,6 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # +from google.api_core import gapic_v1 +from google.api_core import retry as retries +from google.api_core import retry_async as retries_async from typing import ( Any, AsyncIterator, @@ -22,8 +25,18 @@ Tuple, Optional, Iterator, + Union, ) +try: + OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] + OptionalAsyncRetry = Union[ + retries_async.AsyncRetry, gapic_v1.method._MethodDefault, None + ] +except AttributeError: # pragma: NO COVER + OptionalRetry = Union[retries.Retry, object, None] # type: ignore + OptionalAsyncRetry = Union[retries_async.AsyncRetry, object, None] # type: ignore + from google.pubsub_v1.types import pubsub @@ -51,6 +64,8 @@ def __init__( request: pubsub.ListSubscriptionsRequest, response: pubsub.ListSubscriptionsResponse, *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = () ): """Instantiate the pager. @@ -62,12 +77,17 @@ def __init__( The initial request object. response (google.pubsub_v1.types.ListSubscriptionsResponse): The initial response object. + retry (google.api_core.retry.Retry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. """ self._method = method self._request = pubsub.ListSubscriptionsRequest(request) self._response = response + self._retry = retry + self._timeout = timeout self._metadata = metadata def __getattr__(self, name: str) -> Any: @@ -78,7 +98,12 @@ def pages(self) -> Iterator[pubsub.ListSubscriptionsResponse]: yield self._response while self._response.next_page_token: self._request.page_token = self._response.next_page_token - self._response = self._method(self._request, metadata=self._metadata) + self._response = self._method( + self._request, + retry=self._retry, + timeout=self._timeout, + metadata=self._metadata, + ) yield self._response def __iter__(self) -> Iterator[pubsub.Subscription]: @@ -113,6 +138,8 @@ def __init__( request: pubsub.ListSubscriptionsRequest, response: pubsub.ListSubscriptionsResponse, *, + retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = () ): """Instantiates the pager. @@ -124,12 +151,17 @@ def __init__( The initial request object. response (google.pubsub_v1.types.ListSubscriptionsResponse): The initial response object. + retry (google.api_core.retry.AsyncRetry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. """ self._method = method self._request = pubsub.ListSubscriptionsRequest(request) self._response = response + self._retry = retry + self._timeout = timeout self._metadata = metadata def __getattr__(self, name: str) -> Any: @@ -140,7 +172,12 @@ async def pages(self) -> AsyncIterator[pubsub.ListSubscriptionsResponse]: yield self._response while self._response.next_page_token: self._request.page_token = self._response.next_page_token - self._response = await self._method(self._request, metadata=self._metadata) + self._response = await self._method( + self._request, + retry=self._retry, + timeout=self._timeout, + metadata=self._metadata, + ) yield self._response def __aiter__(self) -> AsyncIterator[pubsub.Subscription]: @@ -179,6 +216,8 @@ def __init__( request: pubsub.ListSnapshotsRequest, response: pubsub.ListSnapshotsResponse, *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = () ): """Instantiate the pager. @@ -190,12 +229,17 @@ def __init__( The initial request object. response (google.pubsub_v1.types.ListSnapshotsResponse): The initial response object. + retry (google.api_core.retry.Retry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. """ self._method = method self._request = pubsub.ListSnapshotsRequest(request) self._response = response + self._retry = retry + self._timeout = timeout self._metadata = metadata def __getattr__(self, name: str) -> Any: @@ -206,7 +250,12 @@ def pages(self) -> Iterator[pubsub.ListSnapshotsResponse]: yield self._response while self._response.next_page_token: self._request.page_token = self._response.next_page_token - self._response = self._method(self._request, metadata=self._metadata) + self._response = self._method( + self._request, + retry=self._retry, + timeout=self._timeout, + metadata=self._metadata, + ) yield self._response def __iter__(self) -> Iterator[pubsub.Snapshot]: @@ -241,6 +290,8 @@ def __init__( request: pubsub.ListSnapshotsRequest, response: pubsub.ListSnapshotsResponse, *, + retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, metadata: Sequence[Tuple[str, str]] = () ): """Instantiates the pager. @@ -252,12 +303,17 @@ def __init__( The initial request object. response (google.pubsub_v1.types.ListSnapshotsResponse): The initial response object. + retry (google.api_core.retry.AsyncRetry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. """ self._method = method self._request = pubsub.ListSnapshotsRequest(request) self._response = response + self._retry = retry + self._timeout = timeout self._metadata = metadata def __getattr__(self, name: str) -> Any: @@ -268,7 +324,12 @@ async def pages(self) -> AsyncIterator[pubsub.ListSnapshotsResponse]: yield self._response while self._response.next_page_token: self._request.page_token = self._response.next_page_token - self._response = await self._method(self._request, metadata=self._metadata) + self._response = await self._method( + self._request, + retry=self._retry, + timeout=self._timeout, + metadata=self._metadata, + ) yield self._response def __aiter__(self) -> AsyncIterator[pubsub.Snapshot]: diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index d391b168b..d66015ac4 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.23.0" + "version": "0.1.0" }, "snippets": [ { diff --git a/tests/unit/gapic/pubsub_v1/test_publisher.py b/tests/unit/gapic/pubsub_v1/test_publisher.py index 6f1c95722..5af39dbf8 100644 --- a/tests/unit/gapic/pubsub_v1/test_publisher.py +++ b/tests/unit/gapic/pubsub_v1/test_publisher.py @@ -43,6 +43,7 @@ from google.api_core import grpc_helpers from google.api_core import grpc_helpers_async from google.api_core import path_template +from google.api_core import retry as retries from google.auth import credentials as ga_credentials from google.auth.exceptions import MutualTLSChannelError from google.iam.v1 import iam_policy_pb2 # type: ignore @@ -1261,22 +1262,23 @@ async def test_create_topic_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.create_topic - ] = mock_object + ] = mock_rpc request = {} await client.create_topic(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.create_topic(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -1632,22 +1634,23 @@ async def test_update_topic_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.update_topic - ] = mock_object + ] = mock_rpc request = {} await client.update_topic(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.update_topic(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -2006,22 +2009,23 @@ async def test_publish_async_use_cached_wrapped_rpc(transport: str = "grpc_async ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.publish - ] = mock_object + ] = mock_rpc request = {} await client.publish(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.publish(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -2387,22 +2391,23 @@ async def test_get_topic_async_use_cached_wrapped_rpc(transport: str = "grpc_asy ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.get_topic - ] = mock_object + ] = mock_rpc request = {} await client.get_topic(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.get_topic(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -2755,22 +2760,23 @@ async def test_list_topics_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.list_topics - ] = mock_object + ] = mock_rpc request = {} await client.list_topics(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.list_topics(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -2993,12 +2999,16 @@ def test_list_topics_pager(transport_name: str = "grpc"): ) expected_metadata = () + retry = retries.Retry() + timeout = 5 expected_metadata = tuple(expected_metadata) + ( gapic_v1.routing_header.to_grpc_metadata((("project", ""),)), ) - pager = client.list_topics(request={}) + pager = client.list_topics(request={}, retry=retry, timeout=timeout) assert pager._metadata == expected_metadata + assert pager._retry == retry + assert pager._timeout == timeout results = list(pager) assert len(results) == 6 @@ -3327,22 +3337,23 @@ async def test_list_topic_subscriptions_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.list_topic_subscriptions - ] = mock_object + ] = mock_rpc request = {} await client.list_topic_subscriptions(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.list_topic_subscriptions(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -3579,12 +3590,18 @@ def test_list_topic_subscriptions_pager(transport_name: str = "grpc"): ) expected_metadata = () + retry = retries.Retry() + timeout = 5 expected_metadata = tuple(expected_metadata) + ( gapic_v1.routing_header.to_grpc_metadata((("topic", ""),)), ) - pager = client.list_topic_subscriptions(request={}) + pager = client.list_topic_subscriptions( + request={}, retry=retry, timeout=timeout + ) assert pager._metadata == expected_metadata + assert pager._retry == retry + assert pager._timeout == timeout results = list(pager) assert len(results) == 6 @@ -3918,22 +3935,23 @@ async def test_list_topic_snapshots_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.list_topic_snapshots - ] = mock_object + ] = mock_rpc request = {} await client.list_topic_snapshots(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.list_topic_snapshots(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -4170,12 +4188,16 @@ def test_list_topic_snapshots_pager(transport_name: str = "grpc"): ) expected_metadata = () + retry = retries.Retry() + timeout = 5 expected_metadata = tuple(expected_metadata) + ( gapic_v1.routing_header.to_grpc_metadata((("topic", ""),)), ) - pager = client.list_topic_snapshots(request={}) + pager = client.list_topic_snapshots(request={}, retry=retry, timeout=timeout) assert pager._metadata == expected_metadata + assert pager._retry == retry + assert pager._timeout == timeout results = list(pager) assert len(results) == 6 @@ -4485,22 +4507,23 @@ async def test_delete_topic_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.delete_topic - ] = mock_object + ] = mock_rpc request = {} await client.delete_topic(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.delete_topic(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -4847,22 +4870,23 @@ async def test_detach_subscription_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.detach_subscription - ] = mock_object + ] = mock_rpc request = {} await client.detach_subscription(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.detach_subscription(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio diff --git a/tests/unit/gapic/pubsub_v1/test_schema_service.py b/tests/unit/gapic/pubsub_v1/test_schema_service.py index f44f6846f..7be518378 100644 --- a/tests/unit/gapic/pubsub_v1/test_schema_service.py +++ b/tests/unit/gapic/pubsub_v1/test_schema_service.py @@ -43,6 +43,7 @@ from google.api_core import grpc_helpers from google.api_core import grpc_helpers_async from google.api_core import path_template +from google.api_core import retry as retries from google.auth import credentials as ga_credentials from google.auth.exceptions import MutualTLSChannelError from google.iam.v1 import iam_policy_pb2 # type: ignore @@ -1300,22 +1301,23 @@ async def test_create_schema_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.create_schema - ] = mock_object + ] = mock_rpc request = {} await client.create_schema(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.create_schema(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -1693,22 +1695,23 @@ async def test_get_schema_async_use_cached_wrapped_rpc(transport: str = "grpc_as ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.get_schema - ] = mock_object + ] = mock_rpc request = {} await client.get_schema(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.get_schema(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -2061,22 +2064,23 @@ async def test_list_schemas_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.list_schemas - ] = mock_object + ] = mock_rpc request = {} await client.list_schemas(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.list_schemas(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -2299,12 +2303,16 @@ def test_list_schemas_pager(transport_name: str = "grpc"): ) expected_metadata = () + retry = retries.Retry() + timeout = 5 expected_metadata = tuple(expected_metadata) + ( gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), ) - pager = client.list_schemas(request={}) + pager = client.list_schemas(request={}, retry=retry, timeout=timeout) assert pager._metadata == expected_metadata + assert pager._retry == retry + assert pager._timeout == timeout results = list(pager) assert len(results) == 6 @@ -2630,22 +2638,23 @@ async def test_list_schema_revisions_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.list_schema_revisions - ] = mock_object + ] = mock_rpc request = {} await client.list_schema_revisions(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.list_schema_revisions(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -2880,12 +2889,16 @@ def test_list_schema_revisions_pager(transport_name: str = "grpc"): ) expected_metadata = () + retry = retries.Retry() + timeout = 5 expected_metadata = tuple(expected_metadata) + ( gapic_v1.routing_header.to_grpc_metadata((("name", ""),)), ) - pager = client.list_schema_revisions(request={}) + pager = client.list_schema_revisions(request={}, retry=retry, timeout=timeout) assert pager._metadata == expected_metadata + assert pager._retry == retry + assert pager._timeout == timeout results = list(pager) assert len(results) == 6 @@ -3211,22 +3224,23 @@ async def test_commit_schema_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.commit_schema - ] = mock_object + ] = mock_rpc request = {} await client.commit_schema(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.commit_schema(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -3598,22 +3612,23 @@ async def test_rollback_schema_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.rollback_schema - ] = mock_object + ] = mock_rpc request = {} await client.rollback_schema(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.rollback_schema(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -3998,22 +4013,23 @@ async def test_delete_schema_revision_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.delete_schema_revision - ] = mock_object + ] = mock_rpc request = {} await client.delete_schema_revision(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.delete_schema_revision(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -4377,22 +4393,23 @@ async def test_delete_schema_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.delete_schema - ] = mock_object + ] = mock_rpc request = {} await client.delete_schema(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.delete_schema(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -4727,22 +4744,23 @@ async def test_validate_schema_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.validate_schema - ] = mock_object + ] = mock_rpc request = {} await client.validate_schema(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.validate_schema(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -5097,22 +5115,23 @@ async def test_validate_message_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.validate_message - ] = mock_object + ] = mock_rpc request = {} await client.validate_message(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.validate_message(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py index 0251bda96..4bef862ec 100644 --- a/tests/unit/gapic/pubsub_v1/test_subscriber.py +++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py @@ -44,6 +44,7 @@ from google.api_core import grpc_helpers from google.api_core import grpc_helpers_async from google.api_core import path_template +from google.api_core import retry as retries from google.auth import credentials as ga_credentials from google.auth.exceptions import MutualTLSChannelError from google.iam.v1 import iam_policy_pb2 # type: ignore @@ -1294,22 +1295,23 @@ async def test_create_subscription_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.create_subscription - ] = mock_object + ] = mock_rpc request = {} await client.create_subscription(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.create_subscription(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -1736,22 +1738,23 @@ async def test_get_subscription_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.get_subscription - ] = mock_object + ] = mock_rpc request = {} await client.get_subscription(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.get_subscription(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -2144,22 +2147,23 @@ async def test_update_subscription_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.update_subscription - ] = mock_object + ] = mock_rpc request = {} await client.update_subscription(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.update_subscription(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -2554,22 +2558,23 @@ async def test_list_subscriptions_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.list_subscriptions - ] = mock_object + ] = mock_rpc request = {} await client.list_subscriptions(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.list_subscriptions(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -2804,12 +2809,16 @@ def test_list_subscriptions_pager(transport_name: str = "grpc"): ) expected_metadata = () + retry = retries.Retry() + timeout = 5 expected_metadata = tuple(expected_metadata) + ( gapic_v1.routing_header.to_grpc_metadata((("project", ""),)), ) - pager = client.list_subscriptions(request={}) + pager = client.list_subscriptions(request={}, retry=retry, timeout=timeout) assert pager._metadata == expected_metadata + assert pager._retry == retry + assert pager._timeout == timeout results = list(pager) assert len(results) == 6 @@ -3131,22 +3140,23 @@ async def test_delete_subscription_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.delete_subscription - ] = mock_object + ] = mock_rpc request = {} await client.delete_subscription(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.delete_subscription(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -3501,22 +3511,23 @@ async def test_modify_ack_deadline_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.modify_ack_deadline - ] = mock_object + ] = mock_rpc request = {} await client.modify_ack_deadline(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.modify_ack_deadline(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -3879,22 +3890,23 @@ async def test_acknowledge_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.acknowledge - ] = mock_object + ] = mock_rpc request = {} await client.acknowledge(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.acknowledge(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -4234,22 +4246,23 @@ async def test_pull_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio" ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.pull - ] = mock_object + ] = mock_rpc request = {} await client.pull(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.pull(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -4541,22 +4554,23 @@ async def test_streaming_pull_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.streaming_pull - ] = mock_object + ] = mock_rpc request = [{}] await client.streaming_pull(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.streaming_pull(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -4766,22 +4780,23 @@ async def test_modify_push_config_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.modify_push_config - ] = mock_object + ] = mock_rpc request = {} await client.modify_push_config(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.modify_push_config(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -5144,22 +5159,23 @@ async def test_get_snapshot_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.get_snapshot - ] = mock_object + ] = mock_rpc request = {} await client.get_snapshot(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.get_snapshot(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -5508,22 +5524,23 @@ async def test_list_snapshots_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.list_snapshots - ] = mock_object + ] = mock_rpc request = {} await client.list_snapshots(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.list_snapshots(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -5746,12 +5763,16 @@ def test_list_snapshots_pager(transport_name: str = "grpc"): ) expected_metadata = () + retry = retries.Retry() + timeout = 5 expected_metadata = tuple(expected_metadata) + ( gapic_v1.routing_header.to_grpc_metadata((("project", ""),)), ) - pager = client.list_snapshots(request={}) + pager = client.list_snapshots(request={}, retry=retry, timeout=timeout) assert pager._metadata == expected_metadata + assert pager._retry == retry + assert pager._timeout == timeout results = list(pager) assert len(results) == 6 @@ -6067,22 +6088,23 @@ async def test_create_snapshot_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.create_snapshot - ] = mock_object + ] = mock_rpc request = {} await client.create_snapshot(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.create_snapshot(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -6438,22 +6460,23 @@ async def test_update_snapshot_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.update_snapshot - ] = mock_object + ] = mock_rpc request = {} await client.update_snapshot(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.update_snapshot(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -6803,22 +6826,23 @@ async def test_delete_snapshot_async_use_cached_wrapped_rpc( ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.delete_snapshot - ] = mock_object + ] = mock_rpc request = {} await client.delete_snapshot(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.delete_snapshot(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio @@ -7150,22 +7174,23 @@ async def test_seek_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio" ) # Replace cached wrapped function with mock - mock_object = mock.AsyncMock() + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() client._client._transport._wrapped_methods[ client._client._transport.seek - ] = mock_object + ] = mock_rpc request = {} await client.seek(request) # Establish that the underlying gRPC stub method was called. - assert mock_object.call_count == 1 + assert mock_rpc.call_count == 1 await client.seek(request) # Establish that a new wrapper was not created for this call assert wrapper_fn.call_count == 0 - assert mock_object.call_count == 2 + assert mock_rpc.call_count == 2 @pytest.mark.asyncio From 5402b621ac09b8d6bb10955048e2c0c610a4f68e Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 13:50:59 -0400 Subject: [PATCH 089/190] chore(python): update unittest workflow template (#1242) Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 ++-- .github/workflows/unittest.yml | 1 + .kokoro/docker/docs/Dockerfile | 9 ++++----- .kokoro/publish-docs.sh | 20 ++++++++++---------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index f30cb3775..f8bd8149f 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:52210e0e0559f5ea8c52be148b33504022e1faef4e95fbe4b32d68022af2fa7e -# created: 2024-07-08T19:25:35.862283192Z + digest: sha256:365d92ef2206cfad00a8c5955c36789d0de124e2b6d92a72dd0486315a0f2e57 +# created: 2024-09-04T14:50:52.658171431Z diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index f4a337c49..dd8bd7692 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -30,6 +30,7 @@ jobs: with: name: coverage-artifact-${{ matrix.python }} path: .coverage-${{ matrix.python }} + include-hidden-files: true cover: runs-on: ubuntu-latest diff --git a/.kokoro/docker/docs/Dockerfile b/.kokoro/docker/docs/Dockerfile index 5205308b3..e5410e296 100644 --- a/.kokoro/docker/docs/Dockerfile +++ b/.kokoro/docker/docs/Dockerfile @@ -72,19 +72,18 @@ RUN tar -xvf Python-3.10.14.tgz RUN ./Python-3.10.14/configure --enable-optimizations RUN make altinstall -RUN python3.10 -m venv /venv -ENV PATH /venv/bin:$PATH +ENV PATH /usr/local/bin/python3.10:$PATH ###################### Install pip RUN wget -O /tmp/get-pip.py 'https://bootstrap.pypa.io/get-pip.py' \ - && python3 /tmp/get-pip.py \ + && python3.10 /tmp/get-pip.py \ && rm /tmp/get-pip.py # Test pip -RUN python3 -m pip +RUN python3.10 -m pip # Install build requirements COPY requirements.txt /requirements.txt -RUN python3 -m pip install --require-hashes -r requirements.txt +RUN python3.10 -m pip install --require-hashes -r requirements.txt CMD ["python3.10"] diff --git a/.kokoro/publish-docs.sh b/.kokoro/publish-docs.sh index 38f083f05..233205d58 100755 --- a/.kokoro/publish-docs.sh +++ b/.kokoro/publish-docs.sh @@ -21,18 +21,18 @@ export PYTHONUNBUFFERED=1 export PATH="${HOME}/.local/bin:${PATH}" # Install nox -python3 -m pip install --require-hashes -r .kokoro/requirements.txt -python3 -m nox --version +python3.10 -m pip install --require-hashes -r .kokoro/requirements.txt +python3.10 -m nox --version # build docs nox -s docs # create metadata -python3 -m docuploader create-metadata \ +python3.10 -m docuploader create-metadata \ --name=$(jq --raw-output '.name // empty' .repo-metadata.json) \ - --version=$(python3 setup.py --version) \ + --version=$(python3.10 setup.py --version) \ --language=$(jq --raw-output '.language // empty' .repo-metadata.json) \ - --distribution-name=$(python3 setup.py --name) \ + --distribution-name=$(python3.10 setup.py --name) \ --product-page=$(jq --raw-output '.product_documentation // empty' .repo-metadata.json) \ --github-repository=$(jq --raw-output '.repo // empty' .repo-metadata.json) \ --issue-tracker=$(jq --raw-output '.issue_tracker // empty' .repo-metadata.json) @@ -40,18 +40,18 @@ python3 -m docuploader create-metadata \ cat docs.metadata # upload docs -python3 -m docuploader upload docs/_build/html --metadata-file docs.metadata --staging-bucket "${STAGING_BUCKET}" +python3.10 -m docuploader upload docs/_build/html --metadata-file docs.metadata --staging-bucket "${STAGING_BUCKET}" # docfx yaml files nox -s docfx # create metadata. -python3 -m docuploader create-metadata \ +python3.10 -m docuploader create-metadata \ --name=$(jq --raw-output '.name // empty' .repo-metadata.json) \ - --version=$(python3 setup.py --version) \ + --version=$(python3.10 setup.py --version) \ --language=$(jq --raw-output '.language // empty' .repo-metadata.json) \ - --distribution-name=$(python3 setup.py --name) \ + --distribution-name=$(python3.10 setup.py --name) \ --product-page=$(jq --raw-output '.product_documentation // empty' .repo-metadata.json) \ --github-repository=$(jq --raw-output '.repo // empty' .repo-metadata.json) \ --issue-tracker=$(jq --raw-output '.issue_tracker // empty' .repo-metadata.json) @@ -59,4 +59,4 @@ python3 -m docuploader create-metadata \ cat docs.metadata # upload docs -python3 -m docuploader upload docs/_build/html/docfx_yaml --metadata-file docs.metadata --destination-prefix docfx --staging-bucket "${V2_STAGING_BUCKET}" +python3.10 -m docuploader upload docs/_build/html/docfx_yaml --metadata-file docs.metadata --destination-prefix docfx --staging-bucket "${V2_STAGING_BUCKET}" From 49c7c612ab091de63adabd59ba3b8432879fbd3e Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Mon, 9 Sep 2024 18:21:46 +0200 Subject: [PATCH 090/190] chore(deps): update all dependencies (#1227) Co-authored-by: Owl Bot --- samples/snippets/requirements-test.txt | 4 ++-- samples/snippets/requirements.txt | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index 43e018272..c705889ef 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1,7 +1,7 @@ backoff==2.2.1 pytest===7.4.4; python_version == '3.7' -pytest==8.2.2; python_version >= '3.8' +pytest==8.3.2; python_version >= '3.8' mock==5.1.0 flaky==3.8.1 google-cloud-bigquery==3.25.0 -google-cloud-storage==2.17.0 +google-cloud-storage==2.18.2 diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 0f8d2f938..1a6ec636c 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,5 +1,5 @@ -google-cloud-pubsub==2.22.0 -avro==1.11.3 +google-cloud-pubsub==2.23.0 +avro==1.12.0 protobuf===4.24.4; python_version == '3.7' -protobuf==5.27.2; python_version >= '3.8' -avro==1.11.3 +protobuf==5.27.4; python_version >= '3.8' +avro==1.12.0 From ced4f527c7f918a87d1b89c2b5da59dbdf00e2c3 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Mon, 9 Sep 2024 15:35:56 -0400 Subject: [PATCH 091/190] fix: Replace asserts with None checks for graceful shutdown (#1244) --- .../_protocol/streaming_pull_manager.py | 7 ++- .../subscriber/test_streaming_pull_manager.py | 56 +++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py index b8531db17..c01dd7f2e 100644 --- a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py +++ b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py @@ -1104,8 +1104,11 @@ def _on_response(self, response: gapic_types.StreamingPullResponse) -> None: ) with self._pause_resume_lock: - assert self._scheduler is not None - assert self._leaser is not None + if self._scheduler is None or self._leaser is None: + _LOGGER.debug( + f"self._scheduler={self._scheduler} or self._leaser={self._leaser} is None. Stopping further processing." + ) + return for received_message in received_messages: if ( diff --git a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py index 278f3e88e..d4ce2cfdb 100644 --- a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py +++ b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py @@ -1375,6 +1375,62 @@ def test_close_blocking_scheduler_shutdown(): scheduler.shutdown.assert_called_once_with(await_msg_callbacks=True) +def test__on_response_none_scheduler(): + manager, _, _, _, _, _ = make_running_manager() + + manager._callback = mock.sentinel.callback + manager._scheduler = None + # Set up the messages. + response = gapic_types.StreamingPullResponse( + received_messages=[ + gapic_types.ReceivedMessage( + ack_id="ack1", + message=gapic_types.PubsubMessage(data=b"foo", message_id="1"), + ), + gapic_types.ReceivedMessage( + ack_id="ack2", + message=gapic_types.PubsubMessage(data=b"bar", message_id="2"), + delivery_attempt=6, + ), + ] + ) + + manager._maybe_release_messages = mock.Mock() + + # adjust message bookkeeping in leaser + fake_leaser_add(leaser, init_msg_count=0, assumed_msg_size=42) + manager._on_response(response) + + manager._maybe_release_messages.assert_not_called + + +def test__on_response_none_leaser(): + manager, _, _, _, _, _ = make_running_manager() + + manager._callback = mock.sentinel.callback + manager._leaser = None + # Set up the messages. + response = gapic_types.StreamingPullResponse( + received_messages=[ + gapic_types.ReceivedMessage( + ack_id="ack1", + message=gapic_types.PubsubMessage(data=b"foo", message_id="1"), + ), + gapic_types.ReceivedMessage( + ack_id="ack2", + message=gapic_types.PubsubMessage(data=b"bar", message_id="2"), + delivery_attempt=6, + ), + ] + ) + + manager._maybe_release_messages = mock.Mock() + + manager._on_response(response) + + manager._maybe_release_messages.assert_not_called + + def test_close_nonblocking_scheduler_shutdown(): manager, _, _, _, _, _ = make_running_manager(await_callbacks_on_shutdown=False) scheduler = manager._scheduler From fcc6d77cb1f424bfe84befdf05b848d5cd181ce1 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:17:52 -0700 Subject: [PATCH 092/190] chore(main): release 2.23.1 (#1246) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b6be5b944..94d5d8cc4 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.23.0" + ".": "2.23.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a410d0dc4..93db31182 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.23.1](https://github.com/googleapis/python-pubsub/compare/v2.23.0...v2.23.1) (2024-09-09) + + +### Bug Fixes + +* Replace asserts with None checks for graceful shutdown ([#1244](https://github.com/googleapis/python-pubsub/issues/1244)) ([ced4f52](https://github.com/googleapis/python-pubsub/commit/ced4f527c7f918a87d1b89c2b5da59dbdf00e2c3)) + ## [2.23.0](https://github.com/googleapis/python-pubsub/compare/v2.22.0...v2.23.0) (2024-07-29) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index f01e1d3a5..008f4dd36 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.23.0" # {x-release-please-version} +__version__ = "2.23.1" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index f01e1d3a5..008f4dd36 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.23.0" # {x-release-please-version} +__version__ = "2.23.1" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index d66015ac4..c1602f5ba 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "0.1.0" + "version": "2.23.1" }, "snippets": [ { From eafc1c87e0881cec5d1b689b24798a86eb286957 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 10 Sep 2024 01:19:13 +0200 Subject: [PATCH 093/190] chore(deps): update all dependencies (#1245) Co-authored-by: Owl Bot Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 1a6ec636c..b2dfe2d92 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,5 +1,5 @@ google-cloud-pubsub==2.23.0 avro==1.12.0 protobuf===4.24.4; python_version == '3.7' -protobuf==5.27.4; python_version >= '3.8' +protobuf==5.28.0; python_version >= '3.8' avro==1.12.0 From bb5f3d1a7df2d661cccc336edc8eceb2161c6921 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:59:20 -0400 Subject: [PATCH 094/190] feat: Open Telemetry Publish Side Support (#1241) --- .../pubsub_v1/open_telemetry/__init__.py | 13 + .../open_telemetry/context_propagation.py | 39 ++ .../open_telemetry/publish_message_wrapper.py | 142 +++++ .../cloud/pubsub_v1/publisher/_batch/base.py | 8 +- .../pubsub_v1/publisher/_batch/thread.py | 111 +++- .../publisher/_sequencer/ordered_sequencer.py | 14 +- .../_sequencer/unordered_sequencer.py | 12 +- google/cloud/pubsub_v1/publisher/client.py | 139 ++++- google/cloud/pubsub_v1/types.py | 3 + setup.py | 2 + tests/unit/pubsub_v1/conftest.py | 22 +- .../pubsub_v1/publisher/batch/test_base.py | 5 +- .../pubsub_v1/publisher/batch/test_thread.py | 515 +++++++++++++++--- .../sequencer/test_ordered_sequencer.py | 7 +- .../sequencer/test_unordered_sequencer.py | 11 +- .../publisher/test_publish_message_wrapper.py | 55 ++ .../publisher/test_publisher_client.py | 265 ++++++++- 17 files changed, 1220 insertions(+), 143 deletions(-) create mode 100644 google/cloud/pubsub_v1/open_telemetry/__init__.py create mode 100644 google/cloud/pubsub_v1/open_telemetry/context_propagation.py create mode 100644 google/cloud/pubsub_v1/open_telemetry/publish_message_wrapper.py create mode 100644 tests/unit/pubsub_v1/publisher/test_publish_message_wrapper.py diff --git a/google/cloud/pubsub_v1/open_telemetry/__init__.py b/google/cloud/pubsub_v1/open_telemetry/__init__.py new file mode 100644 index 000000000..e88bb5dbb --- /dev/null +++ b/google/cloud/pubsub_v1/open_telemetry/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2024, Google LLC All rights reserved. +# +# 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. diff --git a/google/cloud/pubsub_v1/open_telemetry/context_propagation.py b/google/cloud/pubsub_v1/open_telemetry/context_propagation.py new file mode 100644 index 000000000..37fad3e20 --- /dev/null +++ b/google/cloud/pubsub_v1/open_telemetry/context_propagation.py @@ -0,0 +1,39 @@ +# Copyright 2024, Google LLC All rights reserved. +# +# 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. + +from opentelemetry.propagators.textmap import Setter + +from google.pubsub_v1 import PubsubMessage + + +class OpenTelemetryContextSetter(Setter): + """ + Used by Open Telemetry for context propagation. + """ + + def set(self, carrier: PubsubMessage, key: str, value: str) -> None: + """ + Injects trace context into Pub/Sub message attributes with + "googclient_" prefix. + + Args: + carrier(PubsubMessage): The Pub/Sub message which is the carrier of Open Telemetry + data. + key(str): The key for which the Open Telemetry context data needs to be set. + value(str): The Open Telemetry context value to be set. + + Returns: + None + """ + carrier.attributes["googclient_" + key] = value diff --git a/google/cloud/pubsub_v1/open_telemetry/publish_message_wrapper.py b/google/cloud/pubsub_v1/open_telemetry/publish_message_wrapper.py new file mode 100644 index 000000000..e03a8f800 --- /dev/null +++ b/google/cloud/pubsub_v1/open_telemetry/publish_message_wrapper.py @@ -0,0 +1,142 @@ +# Copyright 2017, Google LLC All rights reserved. +# +# 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 sys +from datetime import datetime +from typing import Optional + +from opentelemetry import trace +from opentelemetry.trace.propagation import set_span_in_context +from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator + +from google.pubsub_v1 import types as gapic_types +from google.cloud.pubsub_v1.open_telemetry.context_propagation import ( + OpenTelemetryContextSetter, +) + + +class PublishMessageWrapper: + _OPEN_TELEMETRY_TRACER_NAME: str = "google.cloud.pubsub_v1" + _OPEN_TELEMETRY_MESSAGING_SYSTEM: str = "gcp_pubsub" + _OPEN_TELEMETRY_PUBLISHER_BATCHING = "publisher batching" + + _PUBLISH_START_EVENT: str = "publish start" + _PUBLISH_FLOW_CONTROL: str = "publisher flow control" + + def __init__(self, message: gapic_types.PubsubMessage): + self._message: gapic_types.PubsubMessage = message + self._create_span: Optional[trace.Span] = None + self._flow_control_span: Optional[trace.Span] = None + self._batching_span: Optional[trace.Span] = None + + @property + def message(self): + return self._message + + @message.setter # type: ignore[no-redef] # resetting message value is intentional here + def message(self, message: gapic_types.PubsubMessage): + self._message = message + + @property + def create_span(self): + return self._create_span + + def __eq__(self, other): # pragma: NO COVER + """Used for pytest asserts to compare two PublishMessageWrapper objects with the same message""" + if isinstance(self, other.__class__): + return self.message == other.message + return False + + def start_create_span(self, topic: str, ordering_key: str) -> None: + tracer = trace.get_tracer(self._OPEN_TELEMETRY_TRACER_NAME) + assert len(topic.split("/")) == 4 + topic_short_name = topic.split("/")[3] + with tracer.start_as_current_span( + name=f"{topic_short_name} create", + attributes={ + "messaging.system": self._OPEN_TELEMETRY_MESSAGING_SYSTEM, + "messaging.destination.name": topic_short_name, + "code.function": "publish", + "messaging.gcp_pubsub.message.ordering_key": ordering_key, + "messaging.operation": "create", + "gcp.project_id": topic.split("/")[1], + "messaging.message.body.size": sys.getsizeof( + self._message.data + ), # sys.getsizeof() used since the attribute expects size of message body in bytes + }, + kind=trace.SpanKind.PRODUCER, + end_on_exit=False, + ) as create_span: + create_span.add_event( + name=self._PUBLISH_START_EVENT, + attributes={ + "timestamp": str(datetime.now()), + }, + ) + self._create_span = create_span + TraceContextTextMapPropagator().inject( + carrier=self._message, + setter=OpenTelemetryContextSetter(), + ) + + def end_create_span(self, exc: Optional[BaseException] = None) -> None: + assert self._create_span is not None + if exc: + self._create_span.record_exception(exception=exc) + self._create_span.set_status( + trace.Status(status_code=trace.StatusCode.ERROR) + ) + self._create_span.end() + + def start_publisher_flow_control_span(self) -> None: + tracer = trace.get_tracer(self._OPEN_TELEMETRY_TRACER_NAME) + assert self._create_span is not None + with tracer.start_as_current_span( + name=self._PUBLISH_FLOW_CONTROL, + kind=trace.SpanKind.INTERNAL, + context=set_span_in_context(self._create_span), + end_on_exit=False, + ) as flow_control_span: + self._flow_control_span = flow_control_span + + def end_publisher_flow_control_span( + self, exc: Optional[BaseException] = None + ) -> None: + assert self._flow_control_span is not None + if exc: + self._flow_control_span.record_exception(exception=exc) + self._flow_control_span.set_status( + trace.Status(status_code=trace.StatusCode.ERROR) + ) + self._flow_control_span.end() + + def start_publisher_batching_span(self) -> None: + assert self._create_span is not None + tracer = trace.get_tracer(self._OPEN_TELEMETRY_TRACER_NAME) + with tracer.start_as_current_span( + name=self._OPEN_TELEMETRY_PUBLISHER_BATCHING, + kind=trace.SpanKind.INTERNAL, + context=set_span_in_context(self._create_span), + end_on_exit=False, + ) as batching_span: + self._batching_span = batching_span + + def end_publisher_batching_span(self, exc: Optional[BaseException] = None) -> None: + assert self._batching_span is not None + if exc: + self._batching_span.record_exception(exception=exc) + self._batching_span.set_status( + trace.Status(status_code=trace.StatusCode.ERROR) + ) + self._batching_span.end() diff --git a/google/cloud/pubsub_v1/publisher/_batch/base.py b/google/cloud/pubsub_v1/publisher/_batch/base.py index 52505996b..c91e0a444 100644 --- a/google/cloud/pubsub_v1/publisher/_batch/base.py +++ b/google/cloud/pubsub_v1/publisher/_batch/base.py @@ -19,6 +19,10 @@ import typing from typing import Optional, Sequence +from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import ( + PublishMessageWrapper, +) + if typing.TYPE_CHECKING: # pragma: NO COVER from google.cloud import pubsub_v1 @@ -54,7 +58,7 @@ class Batch(metaclass=abc.ABCMeta): def __len__(self): """Return the number of messages currently in the batch.""" - return len(self.messages) + return len(self.message_wrappers) @staticmethod @abc.abstractmethod @@ -68,7 +72,7 @@ def make_lock(): # pragma: NO COVER @property @abc.abstractmethod - def messages(self) -> Sequence["gapic_types.PubsubMessage"]: # pragma: NO COVER + def message_wrappers(self) -> Sequence[PublishMessageWrapper]: # pragma: NO COVER """Return the messages currently in the batch. Returns: diff --git a/google/cloud/pubsub_v1/publisher/_batch/thread.py b/google/cloud/pubsub_v1/publisher/_batch/thread.py index 1617f8c90..c4bf67c35 100644 --- a/google/cloud/pubsub_v1/publisher/_batch/thread.py +++ b/google/cloud/pubsub_v1/publisher/_batch/thread.py @@ -19,13 +19,19 @@ import time import typing from typing import Any, Callable, List, Optional, Sequence +from datetime import datetime +from opentelemetry import trace import google.api_core.exceptions from google.api_core import gapic_v1 + from google.cloud.pubsub_v1.publisher import exceptions from google.cloud.pubsub_v1.publisher import futures from google.cloud.pubsub_v1.publisher._batch import base from google.pubsub_v1 import types as gapic_types +from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import ( + PublishMessageWrapper, +) if typing.TYPE_CHECKING: # pragma: NO COVER from google.cloud import pubsub_v1 @@ -85,6 +91,9 @@ class Batch(base.Batch): timeout is used. """ + _OPEN_TELEMETRY_TRACER_NAME: str = "google.cloud.pubsub_v1" + _OPEN_TELEMETRY_MESSAGING_SYSTEM: str = "gcp_pubsub" + def __init__( self, client: "PublisherClient", @@ -108,7 +117,7 @@ def __init__( # status changed from ACCEPTING_MESSAGES to any other # in order to avoid race conditions self._futures: List[futures.Future] = [] - self._messages: List[gapic_types.PubsubMessage] = [] + self._message_wrappers: List[PublishMessageWrapper] = [] self._status = base.BatchStatus.ACCEPTING_MESSAGES # The initial size is not zero, we need to account for the size overhead @@ -119,6 +128,10 @@ def __init__( self._commit_retry = commit_retry self._commit_timeout = commit_timeout + # Publish RPC Span that will be set by method `_start_publish_rpc_span` + # if Open Telemetry is enabled. + self._rpc_span: Optional[trace.Span] = None + @staticmethod def make_lock() -> threading.Lock: """Return a threading lock. @@ -134,9 +147,9 @@ def client(self) -> "PublisherClient": return self._client @property - def messages(self) -> Sequence[gapic_types.PubsubMessage]: - """The messages currently in the batch.""" - return self._messages + def message_wrappers(self) -> Sequence[PublishMessageWrapper]: + """The message wrappers currently in the batch.""" + return self._message_wrappers @property def settings(self) -> "types.BatchSettings": @@ -226,6 +239,38 @@ def _start_commit_thread(self) -> None: ) commit_thread.start() + def _start_publish_rpc_span(self) -> None: + tracer = trace.get_tracer(self._OPEN_TELEMETRY_TRACER_NAME) + links = [] + + for wrapper in self._message_wrappers: + span = wrapper.create_span + # Add links only for sampled spans. + if span.get_span_context().trace_flags.sampled: + links.append(trace.Link(span.get_span_context())) + assert len(self._topic.split("/")) == 4 + topic_short_name = self._topic.split("/")[3] + with tracer.start_as_current_span( + name=f"{topic_short_name} publish", + attributes={ + "messaging.system": self._OPEN_TELEMETRY_MESSAGING_SYSTEM, + "messaging.destination.name": topic_short_name, + "gcp.project_id": self._topic.split("/")[1], + "messaging.batch.message_count": len(self._message_wrappers), + "messaging.operation": "publish", + "code.function": "_commit", + }, + links=links, + kind=trace.SpanKind.CLIENT, + end_on_exit=False, + ) as rpc_span: + ctx = rpc_span.get_span_context() + for wrapper in self._message_wrappers: + span = wrapper.create_span + if span.get_span_context().trace_flags.sampled: + span.add_link(ctx) + self._rpc_span = rpc_span + def _commit(self) -> None: """Actually publish all of the messages on the active batch. @@ -259,7 +304,7 @@ def _commit(self) -> None: # https://github.com/googleapis/google-cloud-python/issues/8036 # Sanity check: If there are no messages, no-op. - if not self._messages: + if not self._message_wrappers: _LOGGER.debug("No messages to publish, exiting commit") self._status = base.BatchStatus.SUCCESS return @@ -270,18 +315,51 @@ def _commit(self) -> None: batch_transport_succeeded = True try: + if self._client.open_telemetry_enabled: + self._start_publish_rpc_span() + # Performs retries for errors defined by the retry configuration. response = self._client._gapic_publish( topic=self._topic, - messages=self._messages, + messages=[wrapper.message for wrapper in self._message_wrappers], retry=self._commit_retry, timeout=self._commit_timeout, ) + + if self._client.open_telemetry_enabled: + assert self._rpc_span is not None + self._rpc_span.end() + end_time = str(datetime.now()) + for message_id, wrapper in zip( + response.message_ids, self._message_wrappers + ): + span = wrapper.create_span + span.add_event( + name="publish end", + attributes={ + "timestamp": end_time, + }, + ) + span.set_attribute(key="messaging.message.id", value=message_id) + wrapper.end_create_span() except google.api_core.exceptions.GoogleAPIError as exc: # We failed to publish, even after retries, so set the exception on # all futures and exit. self._status = base.BatchStatus.ERROR + if self._client.open_telemetry_enabled: + if self._rpc_span: + self._rpc_span.record_exception( + exception=exc, + ) + self._rpc_span.set_status( + trace.Status(status_code=trace.StatusCode.ERROR) + ) + self._rpc_span.end() + + for wrapper in self._message_wrappers: + wrapper.end_create_span(exc=exc) + batch_transport_succeeded = False if self._batch_done_callback is not None: # Failed to publish batch. @@ -326,7 +404,8 @@ def _commit(self) -> None: self._batch_done_callback(batch_transport_succeeded) def publish( - self, message: gapic_types.PubsubMessage + self, + wrapper: PublishMessageWrapper, ) -> Optional["pubsub_v1.publisher.futures.Future"]: """Publish a single message. @@ -338,7 +417,7 @@ def publish( This method is called by :meth:`~.PublisherClient.publish`. Args: - message: The Pub/Sub message. + wrapper: The Pub/Sub message wrapper. Returns: An object conforming to the :class:`~concurrent.futures.Future` interface @@ -351,12 +430,14 @@ def publish( """ # Coerce the type, just in case. - if not isinstance(message, gapic_types.PubsubMessage): + if not isinstance( + wrapper.message, gapic_types.PubsubMessage + ): # pragma: NO COVER # For performance reasons, the message should be constructed by directly # using the raw protobuf class, and only then wrapping it into the # higher-level PubsubMessage class. - vanilla_pb = _raw_proto_pubbsub_message(**message) - message = gapic_types.PubsubMessage.wrap(vanilla_pb) + vanilla_pb = _raw_proto_pubbsub_message(**wrapper.message) + wrapper.message = gapic_types.PubsubMessage.wrap(vanilla_pb) future = None @@ -369,7 +450,7 @@ def publish( return None size_increase = gapic_types.PublishRequest( - messages=[message] + messages=[wrapper.message] )._pb.ByteSize() if (self._base_request_size + size_increase) > _SERVER_PUBLISH_MAX_BYTES: @@ -381,14 +462,14 @@ def publish( raise exceptions.MessageTooLargeError(err_msg) new_size = self._size + size_increase - new_count = len(self._messages) + 1 + new_count = len(self._message_wrappers) + 1 size_limit = min(self.settings.max_bytes, _SERVER_PUBLISH_MAX_BYTES) overflow = new_size > size_limit or new_count >= self.settings.max_messages - if not self._messages or not overflow: + if not self._message_wrappers or not overflow: # Store the actual message in the batch's message queue. - self._messages.append(message) + self._message_wrappers.append(wrapper) self._size = new_size # Track the future on this batch (so that the result of the diff --git a/google/cloud/pubsub_v1/publisher/_sequencer/ordered_sequencer.py b/google/cloud/pubsub_v1/publisher/_sequencer/ordered_sequencer.py index 30c76a44f..9644a1fa2 100644 --- a/google/cloud/pubsub_v1/publisher/_sequencer/ordered_sequencer.py +++ b/google/cloud/pubsub_v1/publisher/_sequencer/ordered_sequencer.py @@ -23,7 +23,9 @@ from google.cloud.pubsub_v1.publisher import exceptions from google.cloud.pubsub_v1.publisher._sequencer import base as sequencer_base from google.cloud.pubsub_v1.publisher._batch import base as batch_base -from google.pubsub_v1 import types as gapic_types +from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import ( + PublishMessageWrapper, +) if typing.TYPE_CHECKING: # pragma: NO COVER from google.cloud.pubsub_v1 import types @@ -262,15 +264,15 @@ def _create_batch( def publish( self, - message: gapic_types.PubsubMessage, + wrapper: PublishMessageWrapper, retry: "OptionalRetry" = gapic_v1.method.DEFAULT, timeout: "types.OptionalTimeout" = gapic_v1.method.DEFAULT, ) -> futures.Future: """Publish message for this ordering key. Args: - message: - The Pub/Sub message. + wrapper: + The Pub/Sub message wrapper. retry: The retry settings to apply when publishing the message. timeout: @@ -317,11 +319,11 @@ def publish( self._ordered_batches.append(new_batch) batch = self._ordered_batches[-1] - future = batch.publish(message) + future = batch.publish(wrapper) while future is None: batch = self._create_batch(commit_retry=retry, commit_timeout=timeout) self._ordered_batches.append(batch) - future = batch.publish(message) + future = batch.publish(wrapper) return future diff --git a/google/cloud/pubsub_v1/publisher/_sequencer/unordered_sequencer.py b/google/cloud/pubsub_v1/publisher/_sequencer/unordered_sequencer.py index 7d57aa821..7dbd3f084 100644 --- a/google/cloud/pubsub_v1/publisher/_sequencer/unordered_sequencer.py +++ b/google/cloud/pubsub_v1/publisher/_sequencer/unordered_sequencer.py @@ -18,7 +18,9 @@ from google.api_core import gapic_v1 from google.cloud.pubsub_v1.publisher._sequencer import base -from google.pubsub_v1 import types as gapic_types +from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import ( + PublishMessageWrapper, +) if typing.TYPE_CHECKING: # pragma: NO COVER from google.cloud.pubsub_v1.publisher import _batch @@ -115,15 +117,15 @@ def _create_batch( def publish( self, - message: gapic_types.PubsubMessage, + wrapper: PublishMessageWrapper, retry: "OptionalRetry" = gapic_v1.method.DEFAULT, timeout: "types.OptionalTimeout" = gapic_v1.method.DEFAULT, ) -> "futures.Future": """Batch message into existing or new batch. Args: - message: - The Pub/Sub message. + wrapper: + The Pub/Sub message wrapper. retry: The retry settings to apply when publishing the message. timeout: @@ -151,7 +153,7 @@ def publish( future = None while future is None: # Might throw MessageTooLargeError - future = batch.publish(message) + future = batch.publish(wrapper) # batch is full, triggering commit_when_full if future is None: batch = self._create_batch(commit_retry=retry, commit_timeout=timeout) diff --git a/google/cloud/pubsub_v1/publisher/client.py b/google/cloud/pubsub_v1/publisher/client.py index 54b353276..481a8472d 100644 --- a/google/cloud/pubsub_v1/publisher/client.py +++ b/google/cloud/pubsub_v1/publisher/client.py @@ -22,6 +22,7 @@ import typing from typing import Any, Dict, Optional, Sequence, Tuple, Type, Union import warnings +import sys from google.api_core import gapic_v1 from google.auth.credentials import AnonymousCredentials # type: ignore @@ -37,6 +38,9 @@ from google.pubsub_v1 import gapic_version as package_version from google.pubsub_v1 import types as gapic_types from google.pubsub_v1.services.publisher import client as publisher_client +from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import ( + PublishMessageWrapper, +) __version__ = package_version.__version__ @@ -153,6 +157,22 @@ def __init__( # The object controlling the message publishing flow self._flow_controller = FlowController(self.publisher_options.flow_control) + self._open_telemetry_enabled = ( + self.publisher_options.enable_open_telemetry_tracing + ) + # OpenTelemetry features used by the library are not supported in Python versions <= 3.7. + # Refer https://github.com/open-telemetry/opentelemetry-python/issues/3993#issuecomment-2211976389 + if ( + self.publisher_options.enable_open_telemetry_tracing + and sys.version_info.major == 3 + and sys.version_info.minor < 8 + ): + warnings.warn( + message="Open Telemetry for Python version 3.7 or lower is not supported. Disabling Open Telemetry tracing.", + category=RuntimeWarning, + ) + self._open_telemetry_enabled = False + @classmethod def from_service_account_file( # type: ignore[override] cls, @@ -209,6 +229,10 @@ def api(self): warnings.warn(msg, category=DeprecationWarning) return super() + @property + def open_telemetry_enabled(self) -> bool: + return self._open_telemetry_enabled + def _get_or_create_sequencer(self, topic: str, ordering_key: str) -> SequencerType: """Get an existing sequencer or create a new one given the (topic, ordering_key) pair. @@ -368,11 +392,41 @@ def publish( # type: ignore[override] ) message = gapic_types.PubsubMessage.wrap(vanilla_pb) + wrapper: PublishMessageWrapper = PublishMessageWrapper(message) + if self._open_telemetry_enabled: + wrapper.start_create_span(topic=topic, ordering_key=ordering_key) + # Messages should go through flow control to prevent excessive # queuing on the client side (depending on the settings). try: + if self._open_telemetry_enabled: + if wrapper: + wrapper.start_publisher_flow_control_span() + else: # pragma: NO COVER + warnings.warn( + message="PubSubMessageWrapper is None. Not starting publisher flow control span.", + category=RuntimeWarning, + ) self._flow_controller.add(message) + if self._open_telemetry_enabled: + if wrapper: + wrapper.end_publisher_flow_control_span() + else: # pragma: NO COVER + warnings.warn( + message="PubSubMessageWrapper is None. Not ending publisher flow control span.", + category=RuntimeWarning, + ) except exceptions.FlowControlLimitError as exc: + if self._open_telemetry_enabled: + if wrapper: + wrapper.end_publisher_flow_control_span(exc) + wrapper.end_create_span(exc) + else: # pragma: NO COVER + warnings.warn( + message="PubSubMessageWrapper is None. Not ending publisher create and flow control spans on FlowControlLimitError.", + category=RuntimeWarning, + ) + future = futures.Future() future.set_exception(exc) return future @@ -386,31 +440,68 @@ def on_publish_done(future): if timeout is gapic_v1.method.DEFAULT: # if custom timeout not passed in timeout = self.publisher_options.timeout + if self._open_telemetry_enabled: + if wrapper: + wrapper.start_publisher_batching_span() + else: # pragma: NO COVER + warnings.warn( + message="PublishMessageWrapper is None. Hence, not starting publisher batching span", + category=RuntimeWarning, + ) with self._batch_lock: - if self._is_stopped: - raise RuntimeError("Cannot publish on a stopped publisher.") - - # Set retry timeout to "infinite" when message ordering is enabled. - # Note that this then also impacts messages added with an empty - # ordering key. - if self._enable_message_ordering: - if retry is gapic_v1.method.DEFAULT: - # use the default retry for the publish GRPC method as a base - transport = self._transport - base_retry = transport._wrapped_methods[transport.publish]._retry - retry = base_retry.with_deadline(2.0**32) - # timeout needs to be overridden and set to infinite in - # addition to the retry deadline since both determine - # the duration for which retries are attempted. - timeout = 2.0**32 - elif retry is not None: - retry = retry.with_deadline(2.0**32) - timeout = 2.0**32 - - # Delegate the publishing to the sequencer. - sequencer = self._get_or_create_sequencer(topic, ordering_key) - future = sequencer.publish(message, retry=retry, timeout=timeout) - future.add_done_callback(on_publish_done) + try: + if self._is_stopped: + raise RuntimeError("Cannot publish on a stopped publisher.") + + # Set retry timeout to "infinite" when message ordering is enabled. + # Note that this then also impacts messages added with an empty + # ordering key. + if self._enable_message_ordering: + if retry is gapic_v1.method.DEFAULT: + # use the default retry for the publish GRPC method as a base + transport = self._transport + base_retry = transport._wrapped_methods[ + transport.publish + ]._retry + retry = base_retry.with_deadline(2.0**32) + # timeout needs to be overridden and set to infinite in + # addition to the retry deadline since both determine + # the duration for which retries are attempted. + timeout = 2.0**32 + elif retry is not None: + retry = retry.with_deadline(2.0**32) + timeout = 2.0**32 + + # Delegate the publishing to the sequencer. + sequencer = self._get_or_create_sequencer(topic, ordering_key) + future = sequencer.publish( + wrapper=wrapper, retry=retry, timeout=timeout + ) + future.add_done_callback(on_publish_done) + except BaseException as be: + # Exceptions can be thrown when attempting to add messages to + # the batch. If they're thrown, record them in publisher + # batching and create span, end the spans and bubble the + # exception up. + if self._open_telemetry_enabled: + if wrapper: + wrapper.end_publisher_batching_span(be) + wrapper.end_create_span(be) + else: # pragma: NO COVER + warnings.warn( + message="PublishMessageWrapper is None. Hence, not recording exception and ending publisher batching span and create span", + category=RuntimeWarning, + ) + raise be + + if self._open_telemetry_enabled: + if wrapper: + wrapper.end_publisher_batching_span() + else: # pragma: NO COVER + warnings.warn( + message="PublishMessageWrapper is None. Hence, not ending publisher batching span", + category=RuntimeWarning, + ) # Create a timer thread if necessary to enforce the batching # timeout. diff --git a/google/cloud/pubsub_v1/types.py b/google/cloud/pubsub_v1/types.py index 3d071a189..c4282e685 100644 --- a/google/cloud/pubsub_v1/types.py +++ b/google/cloud/pubsub_v1/types.py @@ -174,6 +174,9 @@ class PublisherOptions(NamedTuple): "compatible with :class:`~.pubsub_v1.types.TimeoutType`." ) + enable_open_telemetry_tracing: bool = False # disabled by default + """Open Telemetry tracing is enabled if this is set to True.""" + # Define the type class and default values for flow control settings. # diff --git a/setup.py b/setup.py index dbb66cf7c..cc852f7d8 100644 --- a/setup.py +++ b/setup.py @@ -45,6 +45,8 @@ "protobuf>=3.20.2,<6.0.0dev,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5", "grpc-google-iam-v1 >= 0.12.4, < 1.0.0dev", "grpcio-status >= 1.33.2", + "opentelemetry-api", + "opentelemetry-sdk", ] extras = {"libcst": "libcst >= 0.3.10"} url = "https://github.com/googleapis/python-pubsub" diff --git a/tests/unit/pubsub_v1/conftest.py b/tests/unit/pubsub_v1/conftest.py index dc4192931..b44e2fd84 100644 --- a/tests/unit/pubsub_v1/conftest.py +++ b/tests/unit/pubsub_v1/conftest.py @@ -12,9 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -import google.auth.credentials import pytest +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter +from opentelemetry.sdk.trace.export import SimpleSpanProcessor +from opentelemetry import trace +import google.auth.credentials + @pytest.fixture def creds(): @@ -23,3 +28,18 @@ def creds(): GOOGLE_APPLICATION_CREDENTIALS set. """ yield google.auth.credentials.AnonymousCredentials() + + +@pytest.fixture(scope="session", autouse=True) +def set_trace_provider(): + provider = TracerProvider() + trace.set_tracer_provider(provider) + + +@pytest.fixture(scope="function") +def span_exporter(): + exporter = InMemorySpanExporter() + processor = SimpleSpanProcessor(exporter) + provider = trace.get_tracer_provider() + provider.add_span_processor(processor) + yield exporter diff --git a/tests/unit/pubsub_v1/publisher/batch/test_base.py b/tests/unit/pubsub_v1/publisher/batch/test_base.py index a95d72c12..ae5dbea04 100644 --- a/tests/unit/pubsub_v1/publisher/batch/test_base.py +++ b/tests/unit/pubsub_v1/publisher/batch/test_base.py @@ -21,6 +21,9 @@ from google.cloud.pubsub_v1.publisher._batch.base import BatchStatus from google.cloud.pubsub_v1.publisher._batch.thread import Batch from google.pubsub_v1 import types as gapic_types +from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import ( + PublishMessageWrapper, +) def create_batch(status, settings=types.BatchSettings()): @@ -41,5 +44,5 @@ def create_batch(status, settings=types.BatchSettings()): def test_len(): batch = create_batch(status=BatchStatus.ACCEPTING_MESSAGES) assert len(batch) == 0 - batch.publish(gapic_types.PubsubMessage(data=b"foo")) + batch.publish(PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"foo"))) assert len(batch) == 1 diff --git a/tests/unit/pubsub_v1/publisher/batch/test_thread.py b/tests/unit/pubsub_v1/publisher/batch/test_thread.py index 2752d62a2..32eaa3d98 100644 --- a/tests/unit/pubsub_v1/publisher/batch/test_thread.py +++ b/tests/unit/pubsub_v1/publisher/batch/test_thread.py @@ -25,6 +25,9 @@ import pytest +from opentelemetry import trace +from opentelemetry.trace import SpanContext + import google.api_core.exceptions from google.api_core import gapic_v1 from google.auth import credentials @@ -36,10 +39,18 @@ from google.cloud.pubsub_v1.publisher._batch import thread from google.cloud.pubsub_v1.publisher._batch.thread import Batch from google.pubsub_v1 import types as gapic_types +from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import ( + PublishMessageWrapper, +) -def create_client(): - return publisher.Client(credentials=credentials.AnonymousCredentials()) +def create_client(enable_open_telemetry: bool = False): + return publisher.Client( + credentials=credentials.AnonymousCredentials(), + publisher_options=types.PublisherOptions( + enable_open_telemetry_tracing=enable_open_telemetry, + ), + ) def create_batch( @@ -48,7 +59,8 @@ def create_batch( commit_when_full=True, commit_retry=gapic_v1.method.DEFAULT, commit_timeout: gapic_types.TimeoutType = gapic_v1.method.DEFAULT, - **batch_settings + enable_open_telemetry: bool = False, + **batch_settings, ): """Return a batch object suitable for testing. @@ -62,13 +74,14 @@ def create_batch( for the batch commit call. commit_timeout (:class:`~.pubsub_v1.types.TimeoutType`): The timeout to apply to the batch commit call. + enable_open_telemetry (bool): Whether to enable OpenTelemetry. batch_settings (Mapping[str, str]): Arguments passed on to the :class:``~.pubsub_v1.types.BatchSettings`` constructor. Returns: ~.pubsub_v1.publisher.batch.thread.Batch: A batch object. """ - client = create_client() + client = create_client(enable_open_telemetry=enable_open_telemetry) settings = types.BatchSettings(**batch_settings) return Batch( client, @@ -126,8 +139,16 @@ def test_commit_no_op(): def test_blocking__commit(): batch = create_batch() futures = ( - batch.publish({"data": b"This is my message."}), - batch.publish({"data": b"This is another message."}), + batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"This is my message.") + ) + ), + batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"This is another message.") + ) + ), ) # Set up the underlying API publish method to return a PublishResponse. @@ -160,7 +181,11 @@ def test_blocking__commit(): def test_blocking__commit_custom_retry(): batch = create_batch(commit_retry=mock.sentinel.custom_retry) - batch.publish({"data": b"This is my message."}) + batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"This is my message.") + ) + ) # Set up the underlying API publish method to return a PublishResponse. publish_response = gapic_types.PublishResponse(message_ids=["a"]) @@ -182,7 +207,11 @@ def test_blocking__commit_custom_retry(): def test_blocking__commit_custom_timeout(): batch = create_batch(commit_timeout=mock.sentinel.custom_timeout) - batch.publish({"data": b"This is my message."}) + batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"This is my message.") + ) + ) # Set up the underlying API publish method to return a PublishResponse. publish_response = gapic_types.PublishResponse(message_ids=["a"]) @@ -217,13 +246,21 @@ def api_publish_delay(topic="", messages=(), retry=None, timeout=None): ) with api_publish_patch: - batch.publish({"data": b"first message"}) + batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"first message") + ) + ) start = datetime.datetime.now() event_set = api_publish_called.wait(timeout=1.0) if not event_set: # pragma: NO COVER pytest.fail("API publish was not called in time") - batch.publish({"data": b"second message"}) + batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"second message") + ) + ) end = datetime.datetime.now() # While a batch commit in progress, waiting for the API publish call to @@ -266,8 +303,16 @@ def test_blocking__commit_no_messages(): def test_blocking__commit_wrong_messageid_length(): batch = create_batch() futures = ( - batch.publish({"data": b"blah blah blah"}), - batch.publish({"data": b"blah blah blah blah"}), + batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"blah blah blah") + ) + ), + batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"blah blah blah blah") + ) + ), ) # Set up a PublishResponse that only returns one message ID. @@ -287,8 +332,16 @@ def test_blocking__commit_wrong_messageid_length(): def test_block__commmit_api_error(): batch = create_batch() futures = ( - batch.publish({"data": b"blah blah blah"}), - batch.publish({"data": b"blah blah blah blah"}), + batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"blah blah blah") + ) + ), + batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"blah blah blah blah") + ) + ), ) # Make the API throw an error when publishing. @@ -306,8 +359,16 @@ def test_block__commmit_api_error(): def test_block__commmit_retry_error(): batch = create_batch() futures = ( - batch.publish({"data": b"blah blah blah"}), - batch.publish({"data": b"blah blah blah blah"}), + batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"blah blah blah") + ) + ), + batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"blah blah blah blah") + ) + ), ) # Make the API throw an error when publishing. @@ -324,24 +385,31 @@ def test_block__commmit_retry_error(): def test_publish_updating_batch_size(): batch = create_batch(topic="topic_foo") - messages = ( - gapic_types.PubsubMessage(data=b"foobarbaz"), - gapic_types.PubsubMessage(data=b"spameggs"), - gapic_types.PubsubMessage(data=b"1335020400"), + wrappers = ( + PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foobarbaz"), + ), + PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"spameggs"), + ), + PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"1335020400"), + ), ) # Publish each of the messages, which should save them to the batch. - futures = [batch.publish(message) for message in messages] + futures = [batch.publish(wrapper) for wrapper in wrappers] # There should be three messages on the batch, and three futures. - assert len(batch.messages) == 3 + assert len(batch.message_wrappers) == 3 assert batch._futures == futures # The size should have been incremented by the sum of the size # contributions of each message to the PublishRequest. base_request_size = gapic_types.PublishRequest(topic="topic_foo")._pb.ByteSize() expected_request_size = base_request_size + sum( - gapic_types.PublishRequest(messages=[msg])._pb.ByteSize() for msg in messages + gapic_types.PublishRequest(messages=[wrapper.message])._pb.ByteSize() + for wrapper in wrappers ) assert batch.size == expected_request_size @@ -350,68 +418,82 @@ def test_publish_updating_batch_size(): def test_publish(): batch = create_batch() - message = gapic_types.PubsubMessage() - future = batch.publish(message) + wrapper = PublishMessageWrapper(message=gapic_types.PubsubMessage()) + future = batch.publish(wrapper) - assert len(batch.messages) == 1 + assert len(batch.message_wrappers) == 1 assert batch._futures == [future] def test_publish_max_messages_zero(): batch = create_batch(topic="topic_foo", max_messages=0) - - message = gapic_types.PubsubMessage(data=b"foobarbaz") + wrapper = PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foobarbaz"), + ) with mock.patch.object(batch, "commit") as commit: - future = batch.publish(message) + future = batch.publish(wrapper) assert future is not None - assert len(batch.messages) == 1 + assert len(batch.message_wrappers) == 1 assert batch._futures == [future] commit.assert_called_once() def test_publish_max_messages_enforced(): batch = create_batch(topic="topic_foo", max_messages=1) + wrapper = PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foobarbaz") + ) + wrapper2 = PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foobarbaz2") + ) - message = gapic_types.PubsubMessage(data=b"foobarbaz") - message2 = gapic_types.PubsubMessage(data=b"foobarbaz2") - - future = batch.publish(message) - future2 = batch.publish(message2) + future = batch.publish(wrapper) + future2 = batch.publish(wrapper2) assert future is not None assert future2 is None - assert len(batch.messages) == 1 + assert len(batch.message_wrappers) == 1 assert len(batch._futures) == 1 def test_publish_max_bytes_enforced(): batch = create_batch(topic="topic_foo", max_bytes=15) - message = gapic_types.PubsubMessage(data=b"foobarbaz") - message2 = gapic_types.PubsubMessage(data=b"foobarbaz2") + wrapper = PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foobarbaz") + ) + wrapper2 = PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foobarbaz2") + ) - future = batch.publish(message) - future2 = batch.publish(message2) + future = batch.publish(wrapper) + future2 = batch.publish(wrapper2) assert future is not None assert future2 is None - assert len(batch.messages) == 1 + assert len(batch.message_wrappers) == 1 assert len(batch._futures) == 1 def test_publish_exceed_max_messages(): max_messages = 4 batch = create_batch(max_messages=max_messages) - messages = ( - gapic_types.PubsubMessage(data=b"foobarbaz"), - gapic_types.PubsubMessage(data=b"spameggs"), - gapic_types.PubsubMessage(data=b"1335020400"), + wrappers = ( + PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foobarbaz"), + ), + PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"spameggs"), + ), + PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"1335020400"), + ), ) # Publish each of the messages, which should save them to the batch. with mock.patch.object(batch, "commit") as commit: - futures = [batch.publish(message) for message in messages] + futures = [batch.publish(wrapper) for wrapper in wrappers] assert batch._futures == futures assert len(futures) == max_messages - 1 @@ -420,7 +502,11 @@ def test_publish_exceed_max_messages(): # When a fourth message is published, commit should be called. # No future will be returned in this case. - future = batch.publish(gapic_types.PubsubMessage(data=b"last one")) + future = batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"last one") + ) + ) commit.assert_called_once_with() assert future is None @@ -443,28 +529,32 @@ def test_publish_single_message_size_exceeds_server_size_limit(): assert request_size == 1001 # sanity check, just above the (mocked) server limit with pytest.raises(exceptions.MessageTooLargeError): - batch.publish(big_message) + batch.publish(wrapper=PublishMessageWrapper(message=big_message)) @mock.patch.object(thread, "_SERVER_PUBLISH_MAX_BYTES", 1000) def test_publish_total_messages_size_exceeds_server_size_limit(): batch = create_batch(topic="topic_foo", max_messages=10, max_bytes=1500) - messages = ( - gapic_types.PubsubMessage(data=b"x" * 500), - gapic_types.PubsubMessage(data=b"x" * 600), + wrappers = ( + PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"x" * 500), + ), + PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"x" * 600), + ), ) # Sanity check - request size is still below BatchSettings.max_bytes, # but it exceeds the server-side size limit. request_size = gapic_types.PublishRequest( - topic="topic_foo", messages=messages + topic="topic_foo", messages=[wrapper.message for wrapper in wrappers] )._pb.ByteSize() assert 1000 < request_size < 1500 with mock.patch.object(batch, "commit") as fake_commit: - batch.publish(messages[0]) - batch.publish(messages[1]) + batch.publish(wrappers[0]) + batch.publish(wrappers[1]) # The server side limit should kick in and cause a commit. fake_commit.assert_called_once() @@ -472,21 +562,40 @@ def test_publish_total_messages_size_exceeds_server_size_limit(): def test_publish_dict(): batch = create_batch() - future = batch.publish({"data": b"foobarbaz", "attributes": {"spam": "eggs"}}) + future = batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage( + data=b"foobarbaz", + attributes={"spam": "eggs"}, + ), + ) + ) # There should be one message on the batch. - expected_message = gapic_types.PubsubMessage( - data=b"foobarbaz", attributes={"spam": "eggs"} + expected_message_wrapper = PublishMessageWrapper( + message=gapic_types.PubsubMessage( + data=b"foobarbaz", + attributes={"spam": "eggs"}, + ) ) - assert batch.messages == [expected_message] + + assert batch.message_wrappers == [expected_message_wrapper] assert batch._futures == [future] def test_cancel(): batch = create_batch() futures = ( - batch.publish({"data": b"This is my message."}), - batch.publish({"data": b"This is another message."}), + batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"This is my message."), + ), + ), + batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"This is another message."), + ), + ), ) batch.cancel(BatchCancellationReason.PRIOR_ORDERED_MESSAGE_FAILED) @@ -502,19 +611,29 @@ def test_do_not_commit_when_full_when_flag_is_off(): max_messages = 4 # Set commit_when_full flag to False batch = create_batch(max_messages=max_messages, commit_when_full=False) - messages = ( - gapic_types.PubsubMessage(data=b"foobarbaz"), - gapic_types.PubsubMessage(data=b"spameggs"), - gapic_types.PubsubMessage(data=b"1335020400"), + wrappers = ( + PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foobarbaz"), + ), + PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"spameggs"), + ), + PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"1335020400"), + ), ) with mock.patch.object(batch, "commit") as commit: # Publish 3 messages. - futures = [batch.publish(message) for message in messages] + futures = [batch.publish(wrapper) for wrapper in wrappers] assert len(futures) == 3 # When a fourth message is published, commit should not be called. - future = batch.publish(gapic_types.PubsubMessage(data=b"last one")) + future = batch.publish( + wrapper=PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"last one"), + ) + ) assert commit.call_count == 0 assert future is None @@ -534,8 +653,10 @@ def test_batch_done_callback_called_on_success(): batch = create_batch(batch_done_callback=batch_done_callback_tracker) # Ensure messages exist. - message = gapic_types.PubsubMessage(data=b"foobarbaz") - batch.publish(message) + wrapper = PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foobarbaz") + ) + batch.publish(wrapper) # One response for one published message. publish_response = gapic_types.PublishResponse(message_ids=["a"]) @@ -554,8 +675,10 @@ def test_batch_done_callback_called_on_publish_failure(): batch = create_batch(batch_done_callback=batch_done_callback_tracker) # Ensure messages exist. - message = gapic_types.PubsubMessage(data=b"foobarbaz") - batch.publish(message) + wrapper = PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foobarbaz") + ) + batch.publish(wrapper) # One response for one published message. publish_response = gapic_types.PublishResponse(message_ids=["a"]) @@ -580,8 +703,10 @@ def test_batch_done_callback_called_on_publish_response_invalid(): batch = create_batch(batch_done_callback=batch_done_callback_tracker) # Ensure messages exist. - message = gapic_types.PubsubMessage(data=b"foobarbaz") - batch.publish(message) + wrapper = PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foobarbaz"), + ) + batch.publish(wrapper) # No message ids returned in successful publish response -> invalid. publish_response = gapic_types.PublishResponse(message_ids=[]) @@ -593,3 +718,249 @@ def test_batch_done_callback_called_on_publish_response_invalid(): assert batch_done_callback_tracker.called assert not batch_done_callback_tracker.success + + +# Refer https://opentelemetry.io/docs/languages/python/#version-support +@pytest.mark.skipif( + sys.version_info < (3, 8), reason="Open Telemetry requires python3.8 or higher" +) +def test_open_telemetry_commit_publish_rpc_span_none(span_exporter): + """ + Test scenario where OpenTelemetry is enabled, publish RPC + span creation fails(unexpected) and hence batch._rpc_span is None when + attempting to close it. Required for code coverage. + """ + TOPIC = "projects/projectID/topics/topicID" + batch = create_batch(topic=TOPIC, enable_open_telemetry=True) + + message = PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foo"), + ) + message.start_create_span(topic=TOPIC, ordering_key=None) + batch.publish(message) + + # Mock error when publish RPC span creation is attempted. + error = google.api_core.exceptions.InternalServerError("error") + + with mock.patch.object( + type(batch), + "_start_publish_rpc_span", + side_effect=error, + ): + batch._commit() + + assert batch._rpc_span is None + spans = span_exporter.get_finished_spans() + + # Only Create span should be exported, since publish RPC span creation + # should fail with a mock error. + assert len(spans) == 1 + + publish_create_span = spans[0] + assert publish_create_span.status.status_code == trace.status.StatusCode.ERROR + assert publish_create_span.end_time is not None + + assert publish_create_span.name == "topicID create" + # Publish start event and exception event should be present in publish + # create span. + assert len(publish_create_span.events) == 2 + assert publish_create_span.events[0].name == "publish start" + assert publish_create_span.events[1].name == "exception" + + +# Refer https://opentelemetry.io/docs/languages/python/#version-support +@pytest.mark.skipif( + sys.version_info < (3, 8), reason="Open Telemetry requires python3.8 or higher" +) +def test_open_telemetry_commit_publish_rpc_exception(span_exporter): + TOPIC = "projects/projectID/topics/topicID" + batch = create_batch(topic=TOPIC, enable_open_telemetry=True) + + message = PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foo"), + ) + message.start_create_span(topic=TOPIC, ordering_key=None) + batch.publish(message) + + # Mock publish error. + error = google.api_core.exceptions.InternalServerError("error") + + with mock.patch.object( + type(batch.client), + "_gapic_publish", + side_effect=error, + ): + batch._commit() + + spans = span_exporter.get_finished_spans() + # Span 1: Publish RPC span + # Span 2: Create span. + assert len(spans) == 2 + + # Verify both spans recorded error and have ended. + for span in spans: + assert span.status.status_code == trace.status.StatusCode.ERROR + assert span.end_time is not None + + publish_rpc_span = spans[0] + assert publish_rpc_span.name == "topicID publish" + assert len(publish_rpc_span.events) == 1 + assert publish_rpc_span.events[0].name == "exception" + + publish_create_span = spans[1] + assert publish_create_span.name == "topicID create" + # Publish start event and exception event should be present in publish + # create span. + assert len(publish_create_span.events) == 2 + assert publish_create_span.events[0].name == "publish start" + assert publish_create_span.events[1].name == "exception" + + +# Refer https://opentelemetry.io/docs/languages/python/#version-support +@pytest.mark.skipif( + sys.version_info < (3, 8), reason="Open Telemetry requires python3.8 or higher" +) +def test_opentelemetry_commit_sampling(span_exporter): + TOPIC = "projects/projectID/topics/topic" + batch = create_batch( + topic=TOPIC, + enable_open_telemetry=True, + ) + + message1 = PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foo"), + ) + message1.start_create_span(topic=TOPIC, ordering_key=None) + + message2 = PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"bar"), + ) + message2.start_create_span(topic=TOPIC, ordering_key=None) + + # Mock the 'get_span_context' method to return a mock SpanContext + mock_span_context = mock.Mock(spec=SpanContext) + mock_span_context.trace_flags.sampled = False + + batch.publish(message1) + batch.publish(message2) + + publish_response = gapic_types.PublishResponse(message_ids=["a", "b"]) + + # Patch the 'create_span' method to return the mock SpanContext + with mock.patch.object( + message1.create_span, "get_span_context", return_value=mock_span_context + ): + with mock.patch.object( + type(batch.client), "_gapic_publish", return_value=publish_response + ): + batch._commit() + + spans = span_exporter.get_finished_spans() + + # Span 1: Publish RPC span of both messages + # Span 2: Create span of message 1 + # Span 3: Create span of message 2 + assert len(spans) == 3 + + publish_rpc_span, create_span1, create_span2 = spans + + # Verify publish RPC span has only one link corresponding to + # message 2 which is included in the sample. + assert len(publish_rpc_span.links) == 1 + assert len(create_span1.links) == 0 + assert len(create_span2.links) == 1 + assert publish_rpc_span.links[0].context == create_span2.context + assert create_span2.links[0].context == publish_rpc_span.context + + # Verify all spans have ended. + for span in spans: + assert span.end_time is not None + + # Verify both publish create spans have 2 events - publish start and publish + # end. + for span in spans[1:]: + assert len(span.events) == 2 + assert span.events[0].name == "publish start" + assert span.events[1].name == "publish end" + + +@pytest.mark.skipif( + sys.version_info < (3, 8), reason="Open Telemetry requires python3.8 or higher" +) +def test_opentelemetry_commit(span_exporter): + TOPIC = "projects/projectID/topics/topic" + batch = create_batch( + topic=TOPIC, + enable_open_telemetry=True, + ) + + msg1 = PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foo"), + ) + msg2 = PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"bar"), + ) + msg1.start_create_span(topic=TOPIC, ordering_key=None) + msg2.start_create_span(topic=TOPIC, ordering_key=None) + + # Add both messages to the batch. + batch.publish(msg1) + batch.publish(msg2) + + publish_response = gapic_types.PublishResponse(message_ids=["a", "b"]) + with mock.patch.object( + type(batch.client), "_gapic_publish", return_value=publish_response + ): + batch._commit() + + spans = span_exporter.get_finished_spans() + + # Span 1: publish RPC span - closed after publish RPC success. + # Span 2: publisher create span of message 1 - closed after publish RPC success. + # Span 3: publisher create span of message 2 - closed after publish RPC success. + assert len(spans) == 3 + publish_rpc_span, create_span1, create_span2 = spans + + # Verify publish RPC span + assert publish_rpc_span.name == "topic publish" + assert publish_rpc_span.kind == trace.SpanKind.CLIENT + assert publish_rpc_span.end_time is not None + attributes = publish_rpc_span.attributes + assert attributes["messaging.system"] == "gcp_pubsub" + assert attributes["messaging.destination.name"] == "topic" + assert attributes["gcp.project_id"] == "projectID" + assert attributes["messaging.batch.message_count"] == 2 + assert attributes["messaging.operation"] == "publish" + assert attributes["code.function"] == "_commit" + assert publish_rpc_span.parent is None + # Verify the links correspond to the spans of the published messages. + assert len(publish_rpc_span.links) == 2 + assert publish_rpc_span.links[0].context == create_span1.context + assert publish_rpc_span.links[1].context == create_span2.context + assert len(create_span1.links) == 1 + assert create_span1.links[0].context == publish_rpc_span.get_span_context() + assert len(create_span2.links) == 1 + assert create_span2.links[0].context == publish_rpc_span.get_span_context() + + # Verify spans of the published messages. + assert create_span1.name == "topic create" + assert create_span2.name == "topic create" + + # Verify the publish create spans have been closed after publish success. + assert create_span1.end_time is not None + assert create_span2.end_time is not None + + # Verify message IDs returned from gapic publish are added as attributes + # to the publisher create spans of the messages. + assert "messaging.message.id" in create_span1.attributes + assert create_span1.attributes["messaging.message.id"] == "a" + assert "messaging.message.id" in create_span2.attributes + assert create_span2.attributes["messaging.message.id"] == "b" + + # Verify publish end event added to the span + assert len(create_span1.events) == 2 + assert len(create_span2.events) == 2 + assert create_span1.events[0].name == "publish start" + assert create_span1.events[1].name == "publish end" + assert create_span2.events[0].name == "publish start" + assert create_span2.events[1].name == "publish end" diff --git a/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py b/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py index 7570c2970..4377d1447 100644 --- a/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py +++ b/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py @@ -27,12 +27,17 @@ from google.cloud.pubsub_v1 import publisher from google.cloud.pubsub_v1.publisher._sequencer import ordered_sequencer from google.pubsub_v1 import types as gapic_types +from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import ( + PublishMessageWrapper, +) _ORDERING_KEY = "ordering_key_1" def create_message(): - return gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"}) + return PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"}) + ) def create_client(): diff --git a/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py b/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py index 01d9d6ca4..739bae3bd 100644 --- a/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py +++ b/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py @@ -27,10 +27,15 @@ from google.cloud.pubsub_v1.publisher._batch import base from google.cloud.pubsub_v1.publisher._sequencer import unordered_sequencer from google.pubsub_v1 import types as gapic_types +from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import ( + PublishMessageWrapper, +) def create_message(): - return gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"}) + return PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"}) + ) def create_client(): @@ -140,7 +145,9 @@ def test_publish_after_batch_error(): batch = client._batch_class( client, "topic_name", types.BatchSettings(max_latency=float("inf")) ) - batch._messages.append(mock.Mock(name="message")) # Make batch truthy (non-empty). + batch._message_wrappers.append( + mock.Mock(name="message") + ) # Make batch truthy (non-empty). sequencer = unordered_sequencer.UnorderedSequencer(client, "topic_name") sequencer._set_batch(batch) diff --git a/tests/unit/pubsub_v1/publisher/test_publish_message_wrapper.py b/tests/unit/pubsub_v1/publisher/test_publish_message_wrapper.py new file mode 100644 index 000000000..e100950ad --- /dev/null +++ b/tests/unit/pubsub_v1/publisher/test_publish_message_wrapper.py @@ -0,0 +1,55 @@ +# Copyright 2019, Google LLC All rights reserved. +# +# 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 pytest + +from google.pubsub_v1 import types as gapic_types +from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import ( + PublishMessageWrapper, +) + + +def test_message_setter(): + wrapper = PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"foo")) + another_message = gapic_types.PubsubMessage(data=b"bar") + wrapper.message = another_message + + assert wrapper.message == another_message + + +def test_eq(): + wrapper1 = PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"foo")) + wrapper2 = PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"bar")) + wrapper3 = PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"foo")) + + assert wrapper1.__eq__(wrapper2) is False + assert wrapper1.__eq__(wrapper3) is True + + +def test_end_create_span(): + wrapper = PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"foo")) + with pytest.raises(AssertionError): + wrapper.end_create_span() + + +def test_end_publisher_flow_control_span(): + wrapper = PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"foo")) + with pytest.raises(AssertionError): + wrapper.end_publisher_flow_control_span() + + +def test_end_publisher_batching_span(): + wrapper = PublishMessageWrapper(message=gapic_types.PubsubMessage(data=b"foo")) + with pytest.raises(AssertionError): + wrapper.end_publisher_batching_span() diff --git a/tests/unit/pubsub_v1/publisher/test_publisher_client.py b/tests/unit/pubsub_v1/publisher/test_publisher_client.py index 9db5e0ef8..23255db3b 100644 --- a/tests/unit/pubsub_v1/publisher/test_publisher_client.py +++ b/tests/unit/pubsub_v1/publisher/test_publisher_client.py @@ -29,18 +29,24 @@ import pytest import time +from opentelemetry import trace from google.api_core import gapic_v1 from google.api_core import retry as retries from google.api_core.gapic_v1.client_info import METRICS_METADATA_KEY + from google.cloud.pubsub_v1 import publisher from google.cloud.pubsub_v1 import types - from google.cloud.pubsub_v1.publisher import exceptions from google.cloud.pubsub_v1.publisher._sequencer import ordered_sequencer - from google.pubsub_v1 import types as gapic_types from google.pubsub_v1.services.publisher import client as publisher_client from google.pubsub_v1.services.publisher.transports.grpc import PublisherGrpcTransport +from google.cloud.pubsub_v1.open_telemetry.context_propagation import ( + OpenTelemetryContextSetter, +) +from google.cloud.pubsub_v1.open_telemetry.publish_message_wrapper import ( + PublishMessageWrapper, +) def _assert_retries_equal(retry, retry2): @@ -129,6 +135,220 @@ def test_init_w_custom_transport(creds): assert client.batch_settings.max_messages == 100 +@pytest.mark.parametrize( + "enable_open_telemetry", + [ + True, + False, + ], +) +def test_open_telemetry_publisher_options(creds, enable_open_telemetry): + if sys.version_info >= (3, 8) or enable_open_telemetry is False: + client = publisher.Client( + publisher_options=types.PublisherOptions( + enable_open_telemetry_tracing=enable_open_telemetry + ), + credentials=creds, + ) + assert client._open_telemetry_enabled == enable_open_telemetry + else: + # Open Telemetry is not supported and hence disabled for Python + # versions 3.7 or below + with pytest.warns( + RuntimeWarning, + match="Open Telemetry for Python version 3.7 or lower is not supported. Disabling Open Telemetry tracing.", + ): + client = publisher.Client( + publisher_options=types.PublisherOptions( + enable_open_telemetry_tracing=enable_open_telemetry + ), + credentials=creds, + ) + assert client._open_telemetry_enabled is False + + +def test_opentelemetry_context_setter(): + msg = gapic_types.PubsubMessage(data=b"foo") + OpenTelemetryContextSetter().set(carrier=msg, key="key", value="bar") + + assert "googclient_key" in msg.attributes.keys() + + +@pytest.mark.skipif( + sys.version_info < (3, 8), + reason="Open Telemetry not supported below Python version 3.8", +) +def test_opentelemetry_context_propagation(creds, span_exporter): + TOPIC = "projects/projectID/topics/topicID" + client = publisher.Client( + credentials=creds, + publisher_options=types.PublisherOptions( + enable_open_telemetry_tracing=True, + ), + ) + + message_mock = mock.Mock(spec=publisher.flow_controller.FlowController.add) + client._flow_controller.add = message_mock + client.publish(TOPIC, b"data") + + message_mock.assert_called_once() + args = message_mock.call_args.args + assert len(args) == 1 + assert "googclient_traceparent" in args[0].attributes + + +@pytest.mark.skipif( + sys.version_info < (3, 8), + reason="Open Telemetry not supported below Python version 3.8", +) +@pytest.mark.parametrize( + "enable_open_telemetry", + [ + True, + False, + ], +) +def test_opentelemetry_publisher_batching_exception( + creds, span_exporter, enable_open_telemetry +): + client = publisher.Client( + credentials=creds, + publisher_options=types.PublisherOptions( + enable_open_telemetry_tracing=enable_open_telemetry, + ), + ) + + # Throw an exception when sequencer.publish() is called + sequencer = mock.Mock(spec=ordered_sequencer.OrderedSequencer) + sequencer.publish = mock.Mock(side_effect=RuntimeError("some error")) + client._get_or_create_sequencer = mock.Mock(return_value=sequencer) + + TOPIC = "projects/projectID/topics/topicID" + with pytest.raises(RuntimeError): + client.publish(TOPIC, b"message") + + spans = span_exporter.get_finished_spans() + + if enable_open_telemetry: + # Span 1: Publisher Flow Control span + # Span 2: Publisher Batching span + # Span 3: Create Publish span + assert len(spans) == 3 + + flow_control_span, batching_span, create_span = spans + + # Verify batching span contents. + assert batching_span.name == "publisher batching" + assert batching_span.kind == trace.SpanKind.INTERNAL + assert batching_span.parent.span_id == create_span.get_span_context().span_id + + # Verify exception recorded by the publisher batching span. + assert batching_span.status.status_code == trace.StatusCode.ERROR + assert len(batching_span.events) == 1 + assert batching_span.events[0].name == "exception" + + # Verify exception recorded by the publisher create span. + assert create_span.status.status_code == trace.StatusCode.ERROR + assert len(create_span.events) == 2 + assert create_span.events[0].name == "publish start" + assert create_span.events[1].name == "exception" + + # Verify the finished flow control span. + assert flow_control_span.name == "publisher flow control" + assert len(flow_control_span.events) == 0 + else: + assert len(spans) == 0 + + +@pytest.mark.skipif( + sys.version_info < (3, 8), + reason="Open Telemetry not supported below Python version 3.8", +) +def test_opentelemetry_flow_control_exception(creds, span_exporter): + publisher_options = types.PublisherOptions( + flow_control=types.PublishFlowControl( + message_limit=10, + byte_limit=150, + limit_exceeded_behavior=types.LimitExceededBehavior.ERROR, + ), + enable_open_telemetry_tracing=True, + ) + client = publisher.Client(credentials=creds, publisher_options=publisher_options) + + mock_batch = mock.Mock(spec=client._batch_class) + topic = "projects/projectID/topics/topicID" + client._set_batch(topic, mock_batch) + + future1 = client.publish(topic, b"a" * 60) + future2 = client.publish(topic, b"b" * 100) + + future1.result() # no error, still within flow control limits + with pytest.raises(exceptions.FlowControlLimitError): + future2.result() + + spans = span_exporter.get_finished_spans() + # Span 1 = Publisher Flow Control Span of first publish + # Span 2 = Publisher Batching Span of first publish + # Span 3 = Publisher Flow Control Span of second publish(raises FlowControlLimitError) + # Span 4 = Publish Create Span of second publish(raises FlowControlLimitError) + assert len(spans) == 4 + + failed_flow_control_span = spans[2] + finished_publish_create_span = spans[3] + + # Verify failed flow control span values. + assert failed_flow_control_span.name == "publisher flow control" + assert failed_flow_control_span.kind == trace.SpanKind.INTERNAL + assert ( + failed_flow_control_span.parent.span_id + == finished_publish_create_span.get_span_context().span_id + ) + assert failed_flow_control_span.status.status_code == trace.StatusCode.ERROR + + assert len(failed_flow_control_span.events) == 1 + assert failed_flow_control_span.events[0].name == "exception" + + # Verify finished publish create span values + assert finished_publish_create_span.name == "topicID create" + assert finished_publish_create_span.status.status_code == trace.StatusCode.ERROR + assert len(finished_publish_create_span.events) == 2 + assert finished_publish_create_span.events[0].name == "publish start" + assert finished_publish_create_span.events[1].name == "exception" + + +@pytest.mark.skipif( + sys.version_info < (3, 8), + reason="Open Telemetry not supported below Python version 3.8", +) +def test_opentelemetry_publish(creds, span_exporter): + TOPIC = "projects/projectID/topics/topicID" + client = publisher.Client( + credentials=creds, + publisher_options=types.PublisherOptions( + enable_open_telemetry_tracing=True, + ), + ) + + client.publish(TOPIC, b"message") + spans = span_exporter.get_finished_spans() + + # Span 1: Publisher Flow control span + # Span 2: Publisher Batching span + # Publish Create Span would still be active, and hence not exported. + flow_control_span = spans[0] + assert flow_control_span.name == "publisher flow control" + assert flow_control_span.kind == trace.SpanKind.INTERNAL + # Assert the Publisher Flow Control Span has a parent(the Publish Create + # span is still active, and hence unexported. So, the value of parent cannot + # be asserted) + assert flow_control_span.parent is not None + + batching_span = spans[1] + assert batching_span.name == "publisher batching" + assert batching_span.kind == trace.SpanKind.INTERNAL + assert batching_span.parent is not None + + def test_init_w_api_endpoint(creds): client_options = {"api_endpoint": "testendpoint.google.com"} client = publisher.Client(client_options=client_options, credentials=creds) @@ -240,9 +460,17 @@ def test_publish(creds): # Check mock. batch.publish.assert_has_calls( [ - mock.call(gapic_types.PubsubMessage(data=b"spam")), mock.call( - gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"}) + PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"spam"), + ) + ), + mock.call( + PublishMessageWrapper( + message=gapic_types.PubsubMessage( + data=b"foo", attributes={"bar": "baz"} + ) + ) ), ] ) @@ -381,7 +609,9 @@ def test_publish_attrs_bytestring(creds): # The attributes should have been sent as text. batch.publish.assert_called_once_with( - gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"}) + PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"}) + ) ) @@ -421,8 +651,9 @@ def test_publish_new_batch_needed(creds): commit_timeout=gapic_v1.method.DEFAULT, ) message_pb = gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"}) - batch1.publish.assert_called_once_with(message_pb) - batch2.publish.assert_called_once_with(message_pb) + wrapper = PublishMessageWrapper(message=message_pb) + batch1.publish.assert_called_once_with(wrapper) + batch2.publish.assert_called_once_with(wrapper) def test_publish_attrs_type_error(creds): @@ -445,9 +676,9 @@ def test_publish_custom_retry_overrides_configured_retry(creds): client.publish(topic, b"hello!", retry=mock.sentinel.custom_retry) fake_sequencer.publish.assert_called_once_with( - mock.ANY, retry=mock.sentinel.custom_retry, timeout=mock.ANY + wrapper=mock.ANY, retry=mock.sentinel.custom_retry, timeout=mock.ANY ) - message = fake_sequencer.publish.call_args.args[0] + message = fake_sequencer.publish.call_args.kwargs["wrapper"].message assert message.data == b"hello!" @@ -464,9 +695,9 @@ def test_publish_custom_timeout_overrides_configured_timeout(creds): client.publish(topic, b"hello!", timeout=mock.sentinel.custom_timeout) fake_sequencer.publish.assert_called_once_with( - mock.ANY, retry=mock.ANY, timeout=mock.sentinel.custom_timeout + wrapper=mock.ANY, retry=mock.ANY, timeout=mock.sentinel.custom_timeout ) - message = fake_sequencer.publish.call_args.args[0] + message = fake_sequencer.publish.call_args.kwargs["wrapper"].message assert message.data == b"hello!" @@ -626,10 +857,16 @@ def test_publish_with_ordering_key(creds): # Check mock. batch.publish.assert_has_calls( [ - mock.call(gapic_types.PubsubMessage(data=b"spam", ordering_key="k1")), mock.call( - gapic_types.PubsubMessage( - data=b"foo", attributes={"bar": "baz"}, ordering_key="k1" + PublishMessageWrapper( + message=gapic_types.PubsubMessage(data=b"spam", ordering_key="k1") + ), + ), + mock.call( + PublishMessageWrapper( + message=gapic_types.PubsubMessage( + data=b"foo", attributes={"bar": "baz"}, ordering_key="k1" + ) ) ), ] From 1ae49de09996a5cf19f592f996c46e0222d540fc Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:43:51 -0400 Subject: [PATCH 095/190] fix: Fix flaky test (#1254) --- .../publisher/test_publisher_client.py | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/tests/unit/pubsub_v1/publisher/test_publisher_client.py b/tests/unit/pubsub_v1/publisher/test_publisher_client.py index 23255db3b..abc33f8cb 100644 --- a/tests/unit/pubsub_v1/publisher/test_publisher_client.py +++ b/tests/unit/pubsub_v1/publisher/test_publisher_client.py @@ -332,21 +332,26 @@ def test_opentelemetry_publish(creds, span_exporter): client.publish(TOPIC, b"message") spans = span_exporter.get_finished_spans() - # Span 1: Publisher Flow control span - # Span 2: Publisher Batching span - # Publish Create Span would still be active, and hence not exported. - flow_control_span = spans[0] - assert flow_control_span.name == "publisher flow control" - assert flow_control_span.kind == trace.SpanKind.INTERNAL - # Assert the Publisher Flow Control Span has a parent(the Publish Create - # span is still active, and hence unexported. So, the value of parent cannot - # be asserted) - assert flow_control_span.parent is not None - - batching_span = spans[1] - assert batching_span.name == "publisher batching" - assert batching_span.kind == trace.SpanKind.INTERNAL - assert batching_span.parent is not None + # Publisher Flow control and batching spans would be ended in the + # publish() function and are deterministically expected to be in the + # list of exported spans. The Publish Create span and Publish RPC span + # are run async and end at a non-deterministic time. Hence, + # asserting that we have atleast two spans(flow control and batching span) + assert len(spans) >= 2 + flow_control_span = None + batching_span = None + for span in spans: + if span.name == "publisher flow control": + flow_control_span = span + assert flow_control_span.kind == trace.SpanKind.INTERNAL + assert flow_control_span.parent is not None + if span.name == "publisher batching": + batching_span = span + assert batching_span.kind == trace.SpanKind.INTERNAL + assert batching_span.parent is not None + + assert flow_control_span is not None + assert batching_span is not None def test_init_w_api_endpoint(creds): From 1b6f3d284095e138943576de8551df263f73a506 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Tue, 24 Sep 2024 18:54:02 -0400 Subject: [PATCH 096/190] feat: Add OpenTelemetry support for Subscribe Side (#1252) --- .../open_telemetry/context_propagation.py | 18 +- .../open_telemetry/subscribe_opentelemetry.py | 280 ++++++++++++ .../subscriber/_protocol/dispatcher.py | 244 +++++++++- .../pubsub_v1/subscriber/_protocol/leaser.py | 32 +- .../subscriber/_protocol/messages_on_hold.py | 2 + .../subscriber/_protocol/requests.py | 8 + .../_protocol/streaming_pull_manager.py | 140 +++++- google/cloud/pubsub_v1/subscriber/client.py | 38 +- google/cloud/pubsub_v1/subscriber/message.py | 44 +- google/cloud/pubsub_v1/types.py | 32 +- .../pubsub_v1/subscriber/test_dispatcher.py | 427 ++++++++++++++++++ .../unit/pubsub_v1/subscriber/test_leaser.py | 99 ++++ .../unit/pubsub_v1/subscriber/test_message.py | 152 +++++++ .../subscriber/test_messages_on_hold.py | 33 +- .../subscriber/test_streaming_pull_manager.py | 296 +++++++++++- .../test_subscribe_opentelemetry.py | 202 +++++++++ .../subscriber/test_subscriber_client.py | 49 ++ 17 files changed, 2066 insertions(+), 30 deletions(-) create mode 100644 google/cloud/pubsub_v1/open_telemetry/subscribe_opentelemetry.py create mode 100644 tests/unit/pubsub_v1/subscriber/test_subscribe_opentelemetry.py diff --git a/google/cloud/pubsub_v1/open_telemetry/context_propagation.py b/google/cloud/pubsub_v1/open_telemetry/context_propagation.py index 37fad3e20..bfa1aa638 100644 --- a/google/cloud/pubsub_v1/open_telemetry/context_propagation.py +++ b/google/cloud/pubsub_v1/open_telemetry/context_propagation.py @@ -12,7 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from opentelemetry.propagators.textmap import Setter +from typing import Optional, List + +from opentelemetry.propagators.textmap import Setter, Getter from google.pubsub_v1 import PubsubMessage @@ -37,3 +39,17 @@ def set(self, carrier: PubsubMessage, key: str, value: str) -> None: None """ carrier.attributes["googclient_" + key] = value + + +class OpenTelemetryContextGetter(Getter): + """ + Used by Open Telemetry for context propagation. + """ + + def get(self, carrier: PubsubMessage, key: str) -> Optional[List[str]]: + if ("googclient_" + key) not in carrier.attributes: + return None + return [carrier.attributes["googclient_" + key]] + + def keys(self, carrier: PubsubMessage) -> List[str]: + return list(map(str, carrier.attributes.keys())) diff --git a/google/cloud/pubsub_v1/open_telemetry/subscribe_opentelemetry.py b/google/cloud/pubsub_v1/open_telemetry/subscribe_opentelemetry.py new file mode 100644 index 000000000..88870be60 --- /dev/null +++ b/google/cloud/pubsub_v1/open_telemetry/subscribe_opentelemetry.py @@ -0,0 +1,280 @@ +# Copyright 2024, Google LLC All rights reserved. +# +# 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. + +from typing import Optional, List +from datetime import datetime + +from opentelemetry import trace, context +from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator +from opentelemetry.trace.propagation import set_span_in_context + +from google.cloud.pubsub_v1.open_telemetry.context_propagation import ( + OpenTelemetryContextGetter, +) +from google.pubsub_v1.types import PubsubMessage + +_OPEN_TELEMETRY_TRACER_NAME: str = "google.cloud.pubsub_v1" +_OPEN_TELEMETRY_MESSAGING_SYSTEM: str = "gcp_pubsub" + + +class SubscribeOpenTelemetry: + def __init__(self, message: PubsubMessage): + self._message: PubsubMessage = message + + # subscribe span will be initialized by the `start_subscribe_span` + # method. + self._subscribe_span: Optional[trace.Span] = None + + # subscriber concurrency control span will be initialized by the + # `start_subscribe_concurrency_control_span` method. + self._concurrency_control_span: Optional[trace.Span] = None + + # scheduler span will be initialized by the + # `start_subscribe_scheduler_span` method. + self._scheduler_span: Optional[trace.Span] = None + + # This will be set by `start_subscribe_span` method and will be used + # for other spans, such as process span. + self._subscription_id: Optional[str] = None + + # This will be set by `start_process_span` method. + self._process_span: Optional[trace.Span] = None + + # This will be set by `start_subscribe_span` method, if a publisher create span + # context was extracted from trace propagation. And will be used by spans like + # proces span to add links to the publisher create span. + self._publisher_create_span_context: Optional[context.Context] = None + + # This will be set by `start_subscribe_span` method and will be used + # for other spans, such as modack span. + self._project_id: Optional[str] = None + + @property + def subscription_id(self) -> Optional[str]: + return self._subscription_id + + @property + def project_id(self) -> Optional[str]: + return self._project_id + + @property + def subscribe_span(self) -> Optional[trace.Span]: + return self._subscribe_span + + def start_subscribe_span( + self, + subscription: str, + exactly_once_enabled: bool, + ack_id: str, + delivery_attempt: int, + ) -> None: + tracer = trace.get_tracer(_OPEN_TELEMETRY_TRACER_NAME) + parent_span_context = TraceContextTextMapPropagator().extract( + carrier=self._message, + getter=OpenTelemetryContextGetter(), + ) + self._publisher_create_span_context = parent_span_context + split_subscription: List[str] = subscription.split("/") + assert len(split_subscription) == 4 + subscription_short_name = split_subscription[3] + self._project_id = split_subscription[1] + self._subscription_id = subscription_short_name + with tracer.start_as_current_span( + name=f"{subscription_short_name} subscribe", + context=parent_span_context if parent_span_context else None, + kind=trace.SpanKind.CONSUMER, + attributes={ + "messaging.system": _OPEN_TELEMETRY_MESSAGING_SYSTEM, + "messaging.destination.name": subscription_short_name, + "gcp.project_id": subscription.split("/")[1], + "messaging.message.id": self._message.message_id, + "messaging.message.body.size": len(self._message.data), + "messaging.gcp_pubsub.message.ack_id": ack_id, + "messaging.gcp_pubsub.message.ordering_key": self._message.ordering_key, + "messaging.gcp_pubsub.message.exactly_once_delivery": exactly_once_enabled, + "code.function": "_on_response", + "messaging.gcp_pubsub.message.delivery_attempt": delivery_attempt, + }, + end_on_exit=False, + ) as subscribe_span: + self._subscribe_span = subscribe_span + + def add_subscribe_span_event(self, event: str) -> None: + assert self._subscribe_span is not None + self._subscribe_span.add_event( + name=event, + attributes={ + "timestamp": str(datetime.now()), + }, + ) + + def end_subscribe_span(self) -> None: + assert self._subscribe_span is not None + self._subscribe_span.end() + + def set_subscribe_span_result(self, result: str) -> None: + assert self._subscribe_span is not None + self._subscribe_span.set_attribute( + key="messaging.gcp_pubsub.result", + value=result, + ) + + def start_subscribe_concurrency_control_span(self) -> None: + assert self._subscribe_span is not None + tracer = trace.get_tracer(_OPEN_TELEMETRY_TRACER_NAME) + with tracer.start_as_current_span( + name="subscriber concurrency control", + kind=trace.SpanKind.INTERNAL, + context=set_span_in_context(self._subscribe_span), + end_on_exit=False, + ) as concurrency_control_span: + self._concurrency_control_span = concurrency_control_span + + def end_subscribe_concurrency_control_span(self) -> None: + assert self._concurrency_control_span is not None + self._concurrency_control_span.end() + + def start_subscribe_scheduler_span(self) -> None: + assert self._subscribe_span is not None + tracer = trace.get_tracer(_OPEN_TELEMETRY_TRACER_NAME) + with tracer.start_as_current_span( + name="subscriber scheduler", + kind=trace.SpanKind.INTERNAL, + context=set_span_in_context(self._subscribe_span), + end_on_exit=False, + ) as scheduler_span: + self._scheduler_span = scheduler_span + + def end_subscribe_scheduler_span(self) -> None: + assert self._scheduler_span is not None + self._scheduler_span.end() + + def start_process_span(self) -> None: + assert self._subscribe_span is not None + tracer = trace.get_tracer(_OPEN_TELEMETRY_TRACER_NAME) + publish_create_span_link: Optional[trace.Link] = None + if self._publisher_create_span_context: + publish_create_span: trace.Span = trace.get_current_span( + self._publisher_create_span_context + ) + span_context: Optional[ + trace.SpanContext + ] = publish_create_span.get_span_context() + publish_create_span_link = ( + trace.Link(span_context) if span_context else None + ) + + with tracer.start_as_current_span( + name=f"{self._subscription_id} process", + attributes={ + "messaging.system": _OPEN_TELEMETRY_MESSAGING_SYSTEM, + }, + kind=trace.SpanKind.INTERNAL, + context=set_span_in_context(self._subscribe_span), + links=[publish_create_span_link] if publish_create_span_link else None, + end_on_exit=False, + ) as process_span: + self._process_span = process_span + + def end_process_span(self) -> None: + assert self._process_span is not None + self._process_span.end() + + def add_process_span_event(self, event: str) -> None: + assert self._process_span is not None + self._process_span.add_event( + name=event, + attributes={ + "timestamp": str(datetime.now()), + }, + ) + + +def start_modack_span( + subscribe_span_links: List[trace.Link], + subscription_id: Optional[str], + message_count: int, + deadline: float, + project_id: Optional[str], + code_function: str, + receipt_modack: bool, +) -> trace.Span: + assert subscription_id is not None + assert project_id is not None + tracer = trace.get_tracer(_OPEN_TELEMETRY_TRACER_NAME) + with tracer.start_as_current_span( + name=f"{subscription_id} modack", + attributes={ + "messaging.system": _OPEN_TELEMETRY_MESSAGING_SYSTEM, + "messaging.batch.message_count": message_count, + "messaging.gcp_pubsub.message.ack_deadline": deadline, + "messaging.destination.name": subscription_id, + "gcp.project_id": project_id, + "messaging.operation.name": "modack", + "code.function": code_function, + "messaging.gcp_pubsub.is_receipt_modack": receipt_modack, + }, + links=subscribe_span_links, + kind=trace.SpanKind.CLIENT, + end_on_exit=False, + ) as modack_span: + return modack_span + + +def start_ack_span( + subscription_id: str, + message_count: int, + project_id: str, + links: List[trace.Link], +) -> trace.Span: + tracer = trace.get_tracer(_OPEN_TELEMETRY_TRACER_NAME) + with tracer.start_as_current_span( + name=f"{subscription_id} ack", + attributes={ + "messaging.system": _OPEN_TELEMETRY_MESSAGING_SYSTEM, + "messaging.batch.message_count": message_count, + "messaging.operation": "ack", + "gcp.project_id": project_id, + "messaging.destination.name": subscription_id, + "code.function": "ack", + }, + kind=trace.SpanKind.CLIENT, + links=links, + end_on_exit=False, + ) as ack_span: + return ack_span + + +def start_nack_span( + subscription_id: str, + message_count: int, + project_id: str, + links: List[trace.Link], +) -> trace.Span: + tracer = trace.get_tracer(_OPEN_TELEMETRY_TRACER_NAME) + with tracer.start_as_current_span( + name=f"{subscription_id} nack", + attributes={ + "messaging.system": _OPEN_TELEMETRY_MESSAGING_SYSTEM, + "messaging.batch.message_count": message_count, + "messaging.operation": "nack", + "gcp.project_id": project_id, + "messaging.destination.name": subscription_id, + "code.function": "modify_ack_deadline", + }, + kind=trace.SpanKind.CLIENT, + links=links, + end_on_exit=False, + ) as nack_span: + return nack_span diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/dispatcher.py b/google/cloud/pubsub_v1/subscriber/_protocol/dispatcher.py index 15ad4abb3..fe3771432 100644 --- a/google/cloud/pubsub_v1/subscriber/_protocol/dispatcher.py +++ b/google/cloud/pubsub_v1/subscriber/_protocol/dispatcher.py @@ -26,11 +26,17 @@ import warnings from google.api_core.retry import exponential_sleep_generator +from opentelemetry import trace + from google.cloud.pubsub_v1.subscriber._protocol import helper_threads from google.cloud.pubsub_v1.subscriber._protocol import requests from google.cloud.pubsub_v1.subscriber.exceptions import ( AcknowledgeStatus, ) +from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import ( + start_ack_span, + start_nack_span, +) if typing.TYPE_CHECKING: # pragma: NO COVER import queue @@ -232,16 +238,69 @@ def ack(self, items: Sequence[requests.AckRequest]) -> None: items_gen = iter(items) ack_ids_gen = (item.ack_id for item in items) total_chunks = int(math.ceil(len(items) / _ACK_IDS_BATCH_SIZE)) + subscription_id: Optional[str] = None + project_id: Optional[str] = None + for item in items: + if item.opentelemetry_data: + item.opentelemetry_data.add_subscribe_span_event("ack start") + if subscription_id is None: + subscription_id = item.opentelemetry_data.subscription_id + if project_id is None: + project_id = item.opentelemetry_data.project_id for _ in range(total_chunks): ack_reqs_dict = { req.ack_id: req for req in itertools.islice(items_gen, _ACK_IDS_BATCH_SIZE) } + + subscribe_links: List[trace.Link] = [] + subscribe_spans: List[trace.Span] = [] + for ack_req in ack_reqs_dict.values(): + if ack_req.opentelemetry_data: + subscribe_span: Optional[ + trace.Span + ] = ack_req.opentelemetry_data.subscribe_span + if ( + subscribe_span + and subscribe_span.get_span_context().trace_flags.sampled + ): + subscribe_links.append( + trace.Link(subscribe_span.get_span_context()) + ) + subscribe_spans.append(subscribe_span) + ack_span: Optional[trace.Span] = None + if subscription_id and project_id: + ack_span = start_ack_span( + subscription_id, + len(ack_reqs_dict), + project_id, + subscribe_links, + ) + if ( + ack_span and ack_span.get_span_context().trace_flags.sampled + ): # pragma: NO COVER + ack_span_context: trace.SpanContext = ack_span.get_span_context() + for subscribe_span in subscribe_spans: + subscribe_span.add_link( + context=ack_span_context, + attributes={ + "messaging.operation.name": "ack", + }, + ) + requests_completed, requests_to_retry = self._manager.send_unary_ack( ack_ids=list(itertools.islice(ack_ids_gen, _ACK_IDS_BATCH_SIZE)), ack_reqs_dict=ack_reqs_dict, ) + if ack_span: + ack_span.end() + + for completed_ack in requests_completed: + if completed_ack.opentelemetry_data: + completed_ack.opentelemetry_data.add_subscribe_span_event("ack end") + completed_ack.opentelemetry_data.set_subscribe_span_result("acked") + completed_ack.opentelemetry_data.end_subscribe_span() # Remove the completed messages from lease management. self.drop(requests_completed) @@ -267,7 +326,7 @@ def _start_retry_thread(self, thread_name, thread_target): # a back-end timeout error or other permanent failure. retry_thread.start() - def _retry_acks(self, requests_to_retry): + def _retry_acks(self, requests_to_retry: List[requests.AckRequest]): retry_delay_gen = exponential_sleep_generator( initial=_MIN_EXACTLY_ONCE_DELIVERY_ACK_MODACK_RETRY_DURATION_SECS, maximum=_MAX_EXACTLY_ONCE_DELIVERY_ACK_MODACK_RETRY_DURATION_SECS, @@ -282,10 +341,62 @@ def _retry_acks(self, requests_to_retry): time.sleep(time_to_wait) ack_reqs_dict = {req.ack_id: req for req in requests_to_retry} + subscription_id: Optional[str] = None + project_id: Optional[str] = None + subscribe_links: List[trace.Link] = [] + subscribe_spans: List[trace.Span] = [] + for req in requests_to_retry: + if req.opentelemetry_data: + req.opentelemetry_data.add_subscribe_span_event("ack start") + if subscription_id is None: + subscription_id = req.opentelemetry_data.subscription_id + if project_id is None: + project_id = req.opentelemetry_data.project_id + subscribe_span: Optional[ + trace.Span + ] = req.opentelemetry_data.subscribe_span + if ( + subscribe_span + and subscribe_span.get_span_context().trace_flags.sampled + ): + subscribe_links.append( + trace.Link(subscribe_span.get_span_context()) + ) + subscribe_spans.append(subscribe_span) + ack_span: Optional[trace.Span] = None + if subscription_id and project_id: + ack_span = start_ack_span( + subscription_id, + len(ack_reqs_dict), + project_id, + subscribe_links, + ) + if ( + ack_span and ack_span.get_span_context().trace_flags.sampled + ): # pragma: NO COVER + ack_span_context: trace.SpanContext = ack_span.get_span_context() + for subscribe_span in subscribe_spans: + subscribe_span.add_link( + context=ack_span_context, + attributes={ + "messaging.operation.name": "ack", + }, + ) + requests_completed, requests_to_retry = self._manager.send_unary_ack( ack_ids=[req.ack_id for req in requests_to_retry], ack_reqs_dict=ack_reqs_dict, ) + + if ack_span: + ack_span.end() + + for completed_ack in requests_completed: + if completed_ack.opentelemetry_data: + completed_ack.opentelemetry_data.add_subscribe_span_event("ack end") + completed_ack.opentelemetry_data.set_subscribe_span_result("acked") + completed_ack.opentelemetry_data.end_subscribe_span() + assert ( len(requests_to_retry) <= _ACK_IDS_BATCH_SIZE ), "Too many requests to be retried." @@ -336,15 +447,63 @@ def modify_ack_deadline( deadline_seconds_gen = (item.seconds for item in items) total_chunks = int(math.ceil(len(items) / _ACK_IDS_BATCH_SIZE)) + subscription_id: Optional[str] = None + project_id: Optional[str] = None + + for item in items: + if item.opentelemetry_data: + if math.isclose(item.seconds, 0): + item.opentelemetry_data.add_subscribe_span_event("nack start") + if subscription_id is None: + subscription_id = item.opentelemetry_data.subscription_id + if project_id is None: + project_id = item.opentelemetry_data.project_id + else: + item.opentelemetry_data.add_subscribe_span_event("modack start") for _ in range(total_chunks): ack_reqs_dict = { req.ack_id: req for req in itertools.islice(items_gen, _ACK_IDS_BATCH_SIZE) } + subscribe_links: List[trace.Link] = [] + subscribe_spans: List[trace.Span] = [] + for ack_req in ack_reqs_dict.values(): + if ack_req.opentelemetry_data and math.isclose(ack_req.seconds, 0): + subscribe_span: Optional[ + trace.Span + ] = ack_req.opentelemetry_data.subscribe_span + if ( + subscribe_span + and subscribe_span.get_span_context().trace_flags.sampled + ): + subscribe_links.append( + trace.Link(subscribe_span.get_span_context()) + ) + subscribe_spans.append(subscribe_span) + nack_span: Optional[trace.Span] = None + if subscription_id and project_id: + nack_span = start_nack_span( + subscription_id, + len(ack_reqs_dict), + project_id, + subscribe_links, + ) + if ( + nack_span and nack_span.get_span_context().trace_flags.sampled + ): # pragma: NO COVER + nack_span_context: trace.SpanContext = nack_span.get_span_context() + for subscribe_span in subscribe_spans: + subscribe_span.add_link( + context=nack_span_context, + attributes={ + "messaging.operation.name": "nack", + }, + ) requests_to_retry: List[requests.ModAckRequest] + requests_completed: Optional[List[requests.ModAckRequest]] = None if default_deadline is None: # no further work needs to be done for `requests_to_retry` - _, requests_to_retry = self._manager.send_unary_modack( + requests_completed, requests_to_retry = self._manager.send_unary_modack( modify_deadline_ack_ids=list( itertools.islice(ack_ids_gen, _ACK_IDS_BATCH_SIZE) ), @@ -355,7 +514,7 @@ def modify_ack_deadline( default_deadline=None, ) else: - _, requests_to_retry = self._manager.send_unary_modack( + requests_completed, requests_to_retry = self._manager.send_unary_modack( modify_deadline_ack_ids=itertools.islice( ack_ids_gen, _ACK_IDS_BATCH_SIZE ), @@ -363,10 +522,28 @@ def modify_ack_deadline( ack_reqs_dict=ack_reqs_dict, default_deadline=default_deadline, ) + if nack_span: + nack_span.end() assert ( len(requests_to_retry) <= _ACK_IDS_BATCH_SIZE ), "Too many requests to be retried." + for completed_modack in requests_completed: + if completed_modack.opentelemetry_data: + # nack is a modack with 0 extension seconds. + if math.isclose(completed_modack.seconds, 0): + completed_modack.opentelemetry_data.set_subscribe_span_result( + "nacked" + ) + completed_modack.opentelemetry_data.add_subscribe_span_event( + "nack end" + ) + completed_modack.opentelemetry_data.end_subscribe_span() + else: + completed_modack.opentelemetry_data.add_subscribe_span_event( + "modack end" + ) + # Retry on a separate thread so the dispatcher thread isn't blocked # by sleeps. if requests_to_retry: @@ -390,11 +567,67 @@ def _retry_modacks(self, requests_to_retry): time.sleep(time_to_wait) ack_reqs_dict = {req.ack_id: req for req in requests_to_retry} + + subscription_id = None + project_id = None + subscribe_links = [] + subscribe_spans = [] + for ack_req in ack_reqs_dict.values(): + if ack_req.opentelemetry_data and math.isclose(ack_req.seconds, 0): + if subscription_id is None: + subscription_id = ack_req.opentelemetry_data.subscription_id + if project_id is None: + project_id = ack_req.opentelemetry_data.project_id + subscribe_span = ack_req.opentelemetry_data.subscribe_span + if ( + subscribe_span + and subscribe_span.get_span_context().trace_flags.sampled + ): + subscribe_links.append( + trace.Link(subscribe_span.get_span_context()) + ) + subscribe_spans.append(subscribe_span) + nack_span = None + if subscription_id and project_id: + nack_span = start_nack_span( + subscription_id, + len(ack_reqs_dict), + project_id, + subscribe_links, + ) + if ( + nack_span and nack_span.get_span_context().trace_flags.sampled + ): # pragma: NO COVER + nack_span_context: trace.SpanContext = nack_span.get_span_context() + for subscribe_span in subscribe_spans: + subscribe_span.add_link( + context=nack_span_context, + attributes={ + "messaging.operation.name": "nack", + }, + ) requests_completed, requests_to_retry = self._manager.send_unary_modack( modify_deadline_ack_ids=[req.ack_id for req in requests_to_retry], modify_deadline_seconds=[req.seconds for req in requests_to_retry], ack_reqs_dict=ack_reqs_dict, ) + if nack_span: + nack_span.end() + for completed_modack in requests_completed: + if completed_modack.opentelemetry_data: + # nack is a modack with 0 extension seconds. + if math.isclose(completed_modack.seconds, 0): + completed_modack.opentelemetry_data.set_subscribe_span_result( + "nacked" + ) + completed_modack.opentelemetry_data.add_subscribe_span_event( + "nack end" + ) + completed_modack.opentelemetry_data.end_subscribe_span() + else: + completed_modack.opentelemetry_data.add_subscribe_span_event( + "modack end" + ) def nack(self, items: Sequence[requests.NackRequest]) -> None: """Explicitly deny receipt of messages. @@ -405,7 +638,10 @@ def nack(self, items: Sequence[requests.NackRequest]) -> None: self.modify_ack_deadline( [ requests.ModAckRequest( - ack_id=item.ack_id, seconds=0, future=item.future + ack_id=item.ack_id, + seconds=0, + future=item.future, + opentelemetry_data=item.opentelemetry_data, ) for item in items ] diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/leaser.py b/google/cloud/pubsub_v1/subscriber/_protocol/leaser.py index 16018e384..5abdb7081 100644 --- a/google/cloud/pubsub_v1/subscriber/_protocol/leaser.py +++ b/google/cloud/pubsub_v1/subscriber/_protocol/leaser.py @@ -23,6 +23,9 @@ from typing import Dict, Iterable, Optional, Union from google.cloud.pubsub_v1.subscriber._protocol.dispatcher import _MAX_BATCH_LATENCY +from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import ( + SubscribeOpenTelemetry, +) try: from collections.abc import KeysView @@ -50,6 +53,7 @@ class _LeasedMessage(typing.NamedTuple): size: int ordering_key: Optional[str] + opentelemetry_data: Optional[SubscribeOpenTelemetry] class Leaser(object): @@ -98,6 +102,7 @@ def add(self, items: Iterable[requests.LeaseRequest]) -> None: sent_time=float("inf"), size=item.byte_size, ordering_key=item.ordering_key, + opentelemetry_data=item.opentelemetry_data, ) self._bytes += item.byte_size else: @@ -175,6 +180,17 @@ def maintain_leases(self) -> None: "Dropping %s items because they were leased too long.", len(to_drop) ) assert self._manager.dispatcher is not None + for drop_msg in to_drop: + leased_message = leased_messages.get(drop_msg.ack_id) + if leased_message and leased_message.opentelemetry_data: + leased_message.opentelemetry_data.add_process_span_event( + "expired" + ) + leased_message.opentelemetry_data.end_process_span() + leased_message.opentelemetry_data.set_subscribe_span_result( + "expired" + ) + leased_message.opentelemetry_data.end_subscribe_span() self._manager.dispatcher.drop(to_drop) # Remove dropped items from our copy of the leased messages (they @@ -198,14 +214,28 @@ def maintain_leases(self) -> None: # is inactive. assert self._manager.dispatcher is not None ack_id_gen = (ack_id for ack_id in ack_ids) + opentelemetry_data = [ + message.opentelemetry_data + for message in list(leased_messages.values()) + if message.opentelemetry_data + ] expired_ack_ids = self._manager._send_lease_modacks( - ack_id_gen, deadline + ack_id_gen, + deadline, + opentelemetry_data, ) start_time = time.time() # If exactly once delivery is enabled, we should drop all expired ack_ids from lease management. if self._manager._exactly_once_delivery_enabled() and len(expired_ack_ids): assert self._manager.dispatcher is not None + for ack_id in expired_ack_ids: + msg = leased_messages.get(ack_id) + if msg and msg.opentelemetry_data: + msg.opentelemetry_data.add_process_span_event("expired") + msg.opentelemetry_data.end_process_span() + msg.opentelemetry_data.set_subscribe_span_result("expired") + msg.opentelemetry_data.end_subscribe_span() self._manager.dispatcher.drop( [ requests.DropRequest( diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/messages_on_hold.py b/google/cloud/pubsub_v1/subscriber/_protocol/messages_on_hold.py index 63c2edbfa..3d4c2a392 100644 --- a/google/cloud/pubsub_v1/subscriber/_protocol/messages_on_hold.py +++ b/google/cloud/pubsub_v1/subscriber/_protocol/messages_on_hold.py @@ -100,6 +100,8 @@ def put(self, message: "subscriber.message.Message") -> None: Args: message: The message to put on hold. """ + if message.opentelemetry_data: + message.opentelemetry_data.start_subscribe_scheduler_span() self._messages_on_hold.append(message) self._size = self._size + 1 diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/requests.py b/google/cloud/pubsub_v1/subscriber/_protocol/requests.py index 9cd387545..6fd35896b 100644 --- a/google/cloud/pubsub_v1/subscriber/_protocol/requests.py +++ b/google/cloud/pubsub_v1/subscriber/_protocol/requests.py @@ -15,6 +15,10 @@ import typing from typing import NamedTuple, Optional +from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import ( + SubscribeOpenTelemetry, +) + if typing.TYPE_CHECKING: # pragma: NO COVER from google.cloud.pubsub_v1.subscriber import futures @@ -27,6 +31,7 @@ class AckRequest(NamedTuple): time_to_ack: float ordering_key: Optional[str] future: Optional["futures.Future"] + opentelemetry_data: Optional[SubscribeOpenTelemetry] = None class DropRequest(NamedTuple): @@ -39,12 +44,14 @@ class LeaseRequest(NamedTuple): ack_id: str byte_size: int ordering_key: Optional[str] + opentelemetry_data: Optional[SubscribeOpenTelemetry] = None class ModAckRequest(NamedTuple): ack_id: str seconds: float future: Optional["futures.Future"] + opentelemetry_data: Optional[SubscribeOpenTelemetry] = None class NackRequest(NamedTuple): @@ -52,3 +59,4 @@ class NackRequest(NamedTuple): byte_size: int ordering_key: Optional[str] future: Optional["futures.Future"] + opentelemetry_data: Optional[SubscribeOpenTelemetry] = None diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py index c01dd7f2e..4c9e1c20e 100644 --- a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py +++ b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py @@ -23,6 +23,7 @@ from typing import Any, Dict, Callable, Iterable, List, Optional, Set, Tuple import uuid +from opentelemetry import trace import grpc # type: ignore from google.api_core import bidi @@ -38,6 +39,9 @@ AcknowledgeError, AcknowledgeStatus, ) +from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import ( + SubscribeOpenTelemetry, +) import google.cloud.pubsub_v1.subscriber.message from google.cloud.pubsub_v1.subscriber import futures from google.cloud.pubsub_v1.subscriber.scheduler import ThreadScheduler @@ -46,6 +50,9 @@ from google.rpc.error_details_pb2 import ErrorInfo # type: ignore from google.rpc import code_pb2 # type: ignore from google.rpc import status_pb2 +from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import ( + start_modack_span, +) if typing.TYPE_CHECKING: # pragma: NO COVER from google.cloud.pubsub_v1 import subscriber @@ -123,6 +130,9 @@ def _wrap_callback_errors( message: The Pub/Sub message. """ try: + if message.opentelemetry_data: + message.opentelemetry_data.end_subscribe_concurrency_control_span() + message.opentelemetry_data.start_process_span() callback(message) except BaseException as exc: # Note: the likelihood of this failing is extremely low. This just adds @@ -582,7 +592,8 @@ def _maybe_release_messages(self) -> None: msg = self._messages_on_hold.get() if not msg: break - + if msg.opentelemetry_data: + msg.opentelemetry_data.end_subscribe_scheduler_span() self._schedule_message_on_hold(msg) released_ack_ids.append(msg.ack_id) @@ -618,6 +629,8 @@ def _schedule_message_on_hold( ) assert self._scheduler is not None assert self._callback is not None + if msg.opentelemetry_data: + msg.opentelemetry_data.start_subscribe_concurrency_control_span() self._scheduler.schedule(self._callback, msg) def send_unary_ack( @@ -1007,22 +1020,85 @@ def _get_initial_request( return request def _send_lease_modacks( - self, ack_ids: Iterable[str], ack_deadline: float, warn_on_invalid=True + self, + ack_ids: Iterable[str], + ack_deadline: float, + opentelemetry_data: List[SubscribeOpenTelemetry], + warn_on_invalid=True, + receipt_modack: bool = False, ) -> Set[str]: exactly_once_enabled = False + + modack_span: Optional[trace.Span] = None + if self._client.open_telemetry_enabled: + subscribe_span_links: List[trace.Link] = [] + subscribe_spans: List[trace.Span] = [] + subscription_split: List[str] = self._subscription.split("/") + assert len(subscription_split) == 4 + subscription_id: str = subscription_split[3] + project_id: str = subscription_split[1] + for data in opentelemetry_data: + subscribe_span: Optional[trace.Span] = data.subscribe_span + if ( + subscribe_span + and subscribe_span.get_span_context().trace_flags.sampled + ): + subscribe_span_links.append( + trace.Link(subscribe_span.get_span_context()) + ) + subscribe_spans.append(subscribe_span) + modack_span = start_modack_span( + subscribe_span_links, + subscription_id, + len(opentelemetry_data), + ack_deadline, + project_id, + "_send_lease_modacks", + receipt_modack, + ) + if ( + modack_span and modack_span.get_span_context().trace_flags.sampled + ): # pragma: NO COVER + modack_span_context: trace.SpanContext = modack_span.get_span_context() + for subscribe_span in subscribe_spans: + subscribe_span.add_link( + context=modack_span_context, + attributes={ + "messaging.operation.name": "modack", + }, + ) + with self._exactly_once_enabled_lock: exactly_once_enabled = self._exactly_once_enabled if exactly_once_enabled: - items = [ - requests.ModAckRequest(ack_id, ack_deadline, futures.Future()) - for ack_id in ack_ids - ] + eod_items: List[requests.ModAckRequest] = [] + if self._client.open_telemetry_enabled: + for ack_id, data in zip( + ack_ids, opentelemetry_data + ): # pragma: NO COVER # Identical code covered in the same function below + assert data is not None + eod_items.append( + requests.ModAckRequest( + ack_id, + ack_deadline, + futures.Future(), + data, + ) + ) + else: + eod_items = [ + requests.ModAckRequest(ack_id, ack_deadline, futures.Future()) + for ack_id in ack_ids + ] assert self._dispatcher is not None - self._dispatcher.modify_ack_deadline(items, ack_deadline) - + self._dispatcher.modify_ack_deadline(eod_items, ack_deadline) + if ( + modack_span + ): # pragma: NO COVER # Identical code covered in the same function below + modack_span.end() expired_ack_ids = set() - for req in items: + for req in eod_items: try: assert req.future is not None req.future.result() @@ -1039,12 +1115,27 @@ def _send_lease_modacks( expired_ack_ids.add(req.ack_id) return expired_ack_ids else: - items = [ - requests.ModAckRequest(ack_id, self.ack_deadline, None) - for ack_id in ack_ids - ] + items: List[requests.ModAckRequest] = [] + if self._client.open_telemetry_enabled: + for ack_id, data in zip(ack_ids, opentelemetry_data): + assert data is not None + items.append( + requests.ModAckRequest( + ack_id, + self.ack_deadline, + None, + data, + ) + ) + else: + items = [ + requests.ModAckRequest(ack_id, self.ack_deadline, None) + for ack_id in ack_ids + ] assert self._dispatcher is not None self._dispatcher.modify_ack_deadline(items, ack_deadline) + if modack_span: + modack_span.end() return set() def _exactly_once_delivery_enabled(self) -> bool: @@ -1075,6 +1166,18 @@ def _on_response(self, response: gapic_types.StreamingPullResponse) -> None: # protobuf message to significantly gain on attribute access performance. received_messages = response._pb.received_messages + subscribe_opentelemetry: List[SubscribeOpenTelemetry] = [] + if self._client.open_telemetry_enabled: + for received_message in received_messages: + opentelemetry_data = SubscribeOpenTelemetry(received_message.message) + opentelemetry_data.start_subscribe_span( + self._subscription, + response.subscription_properties.exactly_once_delivery_enabled, + received_message.ack_id, + received_message.delivery_attempt, + ) + subscribe_opentelemetry.append(opentelemetry_data) + _LOGGER.debug( "Processing %s received message(s), currently on hold %s (bytes %s).", len(received_messages), @@ -1100,7 +1203,11 @@ def _on_response(self, response: gapic_types.StreamingPullResponse) -> None: # received them. ack_id_gen = (message.ack_id for message in received_messages) expired_ack_ids = self._send_lease_modacks( - ack_id_gen, self.ack_deadline, warn_on_invalid=False + ack_id_gen, + self.ack_deadline, + subscribe_opentelemetry, + warn_on_invalid=False, + receipt_modack=True, ) with self._pause_resume_lock: @@ -1110,6 +1217,7 @@ def _on_response(self, response: gapic_types.StreamingPullResponse) -> None: ) return + i: int = 0 for received_message in received_messages: if ( not self._exactly_once_delivery_enabled() @@ -1122,12 +1230,16 @@ def _on_response(self, response: gapic_types.StreamingPullResponse) -> None: self._scheduler.queue, self._exactly_once_delivery_enabled, ) + if self._client.open_telemetry_enabled: + message.opentelemetry_data = subscribe_opentelemetry[i] + i = i + 1 self._messages_on_hold.put(message) self._on_hold_bytes += message.size req = requests.LeaseRequest( ack_id=message.ack_id, byte_size=message.size, ordering_key=message.ordering_key, + opentelemetry_data=message.opentelemetry_data, ) self._leaser.add([req]) diff --git a/google/cloud/pubsub_v1/subscriber/client.py b/google/cloud/pubsub_v1/subscriber/client.py index 0d0d36a0c..175095077 100644 --- a/google/cloud/pubsub_v1/subscriber/client.py +++ b/google/cloud/pubsub_v1/subscriber/client.py @@ -14,6 +14,7 @@ from __future__ import absolute_import +import sys import os import typing from typing import cast, Any, Callable, Optional, Sequence, Union @@ -67,7 +68,16 @@ class Client(subscriber_client.SubscriberClient): ) """ - def __init__(self, **kwargs: Any): + def __init__( + self, + subscriber_options: Union[types.SubscriberOptions, Sequence] = (), + **kwargs: Any + ): + assert ( + isinstance(subscriber_options, types.SubscriberOptions) + or len(subscriber_options) == 0 + ), "subscriber_options must be of type SubscriberOptions or an empty sequence." + # Sanity check: Is our goal to use the emulator? # If so, create a grpc insecure channel with the emulator host # as the target. @@ -82,6 +92,32 @@ def __init__(self, **kwargs: Any): self._target = self._transport._host self._closed = False + self.subscriber_options = types.SubscriberOptions(*subscriber_options) + + # Set / override Open Telemetry option. + self._open_telemetry_enabled = ( + self.subscriber_options.enable_open_telemetry_tracing + ) + # OpenTelemetry features used by the library are not supported in Python versions <= 3.7. + # Refer https://github.com/open-telemetry/opentelemetry-python/issues/3993#issuecomment-2211976389 + if ( + self.subscriber_options.enable_open_telemetry_tracing + and sys.version_info.major == 3 + and sys.version_info.minor < 8 + ): + warnings.warn( + message="Open Telemetry for Python version 3.7 or lower is not supported. Disabling Open Telemetry tracing.", + category=RuntimeWarning, + ) + self._open_telemetry_enabled = False + + @property + def open_telemetry_enabled(self) -> bool: + """ + Returns True if Open Telemetry is enabled. False otherwise. + """ + return self._open_telemetry_enabled # pragma: NO COVER + @classmethod def from_service_account_file( # type: ignore[override] cls, filename: str, **kwargs: Any diff --git a/google/cloud/pubsub_v1/subscriber/message.py b/google/cloud/pubsub_v1/subscriber/message.py index f744966a2..61f60c4d9 100644 --- a/google/cloud/pubsub_v1/subscriber/message.py +++ b/google/cloud/pubsub_v1/subscriber/message.py @@ -24,6 +24,9 @@ from google.cloud.pubsub_v1.subscriber._protocol import requests from google.cloud.pubsub_v1.subscriber import futures from google.cloud.pubsub_v1.subscriber.exceptions import AcknowledgeStatus +from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import ( + SubscribeOpenTelemetry, +) if typing.TYPE_CHECKING: # pragma: NO COVER @@ -85,6 +88,8 @@ class Message(object): information on this type. publish_time (google.protobuf.timestamp_pb2.Timestamp): The time that this message was originally published. + opentelemetry_data (google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry.SubscribeOpenTelemetry) + Open Telemetry data associated with this message. None if Open Telemetry is not enabled. """ def __init__( @@ -144,6 +149,9 @@ def __init__( self._ordering_key = message.ordering_key self._size = message.ByteSize() + # None if Open Telemetry is disabled. Else contains OpenTelemetry data. + self._opentelemetry_data: Optional[SubscribeOpenTelemetry] = None + def __repr__(self): # Get an abbreviated version of the data. abbv_data = self._message.data @@ -158,6 +166,14 @@ def __repr__(self): pretty_attrs = pretty_attrs.lstrip() return _MESSAGE_REPR.format(abbv_data, str(self.ordering_key), pretty_attrs) + @property + def opentelemetry_data(self): + return self._opentelemetry_data # pragma: NO COVER + + @opentelemetry_data.setter + def opentelemetry_data(self, data): + self._opentelemetry_data = data # pragma: NO COVER + @property def attributes(self) -> "containers.ScalarMap": """Return the attributes of the underlying Pub/Sub Message. @@ -252,6 +268,9 @@ def ack(self) -> None: https://cloud.google.com/pubsub/docs/exactly-once-delivery." """ + if self.opentelemetry_data: + self.opentelemetry_data.add_process_span_event("ack called") + self.opentelemetry_data.end_process_span() time_to_ack = math.ceil(time.time() - self._received_timestamp) self._request_queue.put( requests.AckRequest( @@ -260,6 +279,7 @@ def ack(self) -> None: time_to_ack=time_to_ack, ordering_key=self.ordering_key, future=None, + opentelemetry_data=self.opentelemetry_data, ) ) @@ -302,6 +322,9 @@ def ack_with_response(self) -> "futures.Future": pubsub_v1.subscriber.exceptions.AcknowledgeError exception will be thrown. """ + if self.opentelemetry_data: + self.opentelemetry_data.add_process_span_event("ack called") + self.opentelemetry_data.end_process_span() req_future: Optional[futures.Future] if self._exactly_once_delivery_enabled_func(): future = futures.Future() @@ -317,6 +340,7 @@ def ack_with_response(self) -> "futures.Future": time_to_ack=time_to_ack, ordering_key=self.ordering_key, future=req_future, + opentelemetry_data=self.opentelemetry_data, ) ) return future @@ -357,7 +381,12 @@ def modify_ack_deadline(self, seconds: int) -> None: against. """ self._request_queue.put( - requests.ModAckRequest(ack_id=self._ack_id, seconds=seconds, future=None) + requests.ModAckRequest( + ack_id=self._ack_id, + seconds=seconds, + future=None, + opentelemetry_data=self.opentelemetry_data, + ) ) def modify_ack_deadline_with_response(self, seconds: int) -> "futures.Future": @@ -416,7 +445,10 @@ def modify_ack_deadline_with_response(self, seconds: int) -> "futures.Future": self._request_queue.put( requests.ModAckRequest( - ack_id=self._ack_id, seconds=seconds, future=req_future + ack_id=self._ack_id, + seconds=seconds, + future=req_future, + opentelemetry_data=self.opentelemetry_data, ) ) @@ -429,12 +461,16 @@ def nack(self) -> None: may take place immediately or after a delay, and may arrive at this subscriber or another. """ + if self.opentelemetry_data: + self.opentelemetry_data.add_process_span_event("nack called") + self.opentelemetry_data.end_process_span() self._request_queue.put( requests.NackRequest( ack_id=self._ack_id, byte_size=self.size, ordering_key=self.ordering_key, future=None, + opentelemetry_data=self.opentelemetry_data, ) ) @@ -472,6 +508,9 @@ def nack_with_response(self) -> "futures.Future": will be thrown. """ + if self.opentelemetry_data: + self.opentelemetry_data.add_process_span_event("nack called") + self.opentelemetry_data.end_process_span() req_future: Optional[futures.Future] if self._exactly_once_delivery_enabled_func(): future = futures.Future() @@ -486,6 +525,7 @@ def nack_with_response(self) -> "futures.Future": byte_size=self.size, ordering_key=self.ordering_key, future=req_future, + opentelemetry_data=self.opentelemetry_data, ) ) diff --git a/google/cloud/pubsub_v1/types.py b/google/cloud/pubsub_v1/types.py index c4282e685..7e94a7250 100644 --- a/google/cloud/pubsub_v1/types.py +++ b/google/cloud/pubsub_v1/types.py @@ -131,6 +131,29 @@ class PublishFlowControl(NamedTuple): """The action to take when publish flow control limits are exceeded.""" +# Define the default subscriber options. +# +# This class is used when creating a subscriber client to pass in options +# to enable/disable features. +class SubscriberOptions(NamedTuple): + """ + Options for the subscriber client. + Attributes: + enable_open_telemetry_tracing (bool): + Whether to enable OpenTelemetry tracing. Defaults to False. + """ + + enable_open_telemetry_tracing: bool = False + """ + Whether to enable OpenTelemetry tracing. + + Warning: traces are subject to change. The name and attributes of a span might + change without notice. Only use run traces interactively. Don't use in + automation. Running non-interactive traces can cause problems if the underlying + trace architecture changes without notice. + """ + + # Define the default publisher options. # # This class is used when creating a publisher client to pass in options @@ -175,7 +198,14 @@ class PublisherOptions(NamedTuple): ) enable_open_telemetry_tracing: bool = False # disabled by default - """Open Telemetry tracing is enabled if this is set to True.""" + """ + Open Telemetry tracing is enabled if this is set to True. + + Warning: traces are subject to change. The name and attributes of a span might + change without notice. Only use run traces interactively. Don't use in + automation. Running non-interactive traces can cause problems if the underlying + trace architecture changes without notice. + """ # Define the type class and default values for flow control settings. diff --git a/tests/unit/pubsub_v1/subscriber/test_dispatcher.py b/tests/unit/pubsub_v1/subscriber/test_dispatcher.py index 89d72c61d..5483c48c5 100644 --- a/tests/unit/pubsub_v1/subscriber/test_dispatcher.py +++ b/tests/unit/pubsub_v1/subscriber/test_dispatcher.py @@ -17,11 +17,17 @@ import sys import threading +from opentelemetry import trace + from google.cloud.pubsub_v1.subscriber._protocol import dispatcher from google.cloud.pubsub_v1.subscriber._protocol import helper_threads from google.cloud.pubsub_v1.subscriber._protocol import requests from google.cloud.pubsub_v1.subscriber._protocol import streaming_pull_manager from google.cloud.pubsub_v1.subscriber import futures +from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import ( + SubscribeOpenTelemetry, +) +from google.pubsub_v1.types import PubsubMessage # special case python < 3.8 if sys.version_info.major == 3 and sys.version_info.minor < 8: @@ -365,6 +371,125 @@ def test_unknown_request_type(): dispatcher_.dispatch_callback(items) +def test_opentelemetry_modify_ack_deadline(span_exporter): + manager = mock.create_autospec( + streaming_pull_manager.StreamingPullManager, instance=True + ) + dispatcher_ = dispatcher.Dispatcher(manager, mock.sentinel.queue) + opentelemetry_data = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo")) + opentelemetry_data.start_subscribe_span( + subscription="projects/projectID/subscriptions/subscriptionID", + exactly_once_enabled=True, + ack_id="ack_id", + delivery_attempt=5, + ) + + items = [ + requests.ModAckRequest( + ack_id="ack_id_string", + seconds=60, + future=None, + opentelemetry_data=opentelemetry_data, + ) + ] + manager.send_unary_modack.return_value = (items, []) + dispatcher_.modify_ack_deadline(items) + + # Subscribe span would not have ended as part of a modack. So, end it + # in the test, so that we can export and assert its contents. + opentelemetry_data.end_subscribe_span() + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + subscribe_span = spans[0] + + assert len(subscribe_span.events) == 2 + assert subscribe_span.events[0].name == "modack start" + assert subscribe_span.events[1].name == "modack end" + + +@pytest.mark.skipif( + sys.version_info < (3, 8), + reason="Open Telemetry not supported below Python version 3.8", +) +def test_opentelemetry_ack(span_exporter): + manager = mock.create_autospec( + streaming_pull_manager.StreamingPullManager, instance=True + ) + dispatcher_ = dispatcher.Dispatcher(manager, mock.sentinel.queue) + + data1 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo")) + data1.start_subscribe_span( + subscription="projects/projectID/subscriptions/subscriptionID", + exactly_once_enabled=True, + ack_id="ack_id", + delivery_attempt=5, + ) + data2 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo")) + data2.start_subscribe_span( + subscription="projects/projectID/subscriptions/subscriptionID", + exactly_once_enabled=True, + ack_id="ack_id", + delivery_attempt=5, + ) + items = [ + requests.AckRequest( + ack_id="ack_id_string", + byte_size=0, + time_to_ack=20, + ordering_key="", + future=None, + opentelemetry_data=data1, + ), + requests.AckRequest( + ack_id="ack_id_string2", + byte_size=0, + time_to_ack=20, + ordering_key="", + future=None, + opentelemetry_data=data2, + ), + ] + manager.send_unary_ack.return_value = (items, []) + mock_span_context = mock.Mock(spec=trace.SpanContext) + mock_span_context.trace_flags.sampled = False + with mock.patch.object( + data2._subscribe_span, "get_span_context", return_value=mock_span_context + ): + dispatcher_.ack(items) + + spans = span_exporter.get_finished_spans() + + assert len(spans) == 3 + ack_span = spans[0] + + for subscribe_span in spans[1:]: + assert subscribe_span.attributes["messaging.gcp_pubsub.result"] == "acked" + assert len(subscribe_span.events) == 2 + assert subscribe_span.events[0].name == "ack start" + assert subscribe_span.events[1].name == "ack end" + + # This subscribe span is sampled, so we expect it to be linked to the ack + # span. + assert len(spans[1].links) == 1 + assert spans[1].links[0].context == ack_span.context + assert len(spans[1].links[0].attributes) == 1 + assert spans[1].links[0].attributes["messaging.operation.name"] == "ack" + # This subscribe span is not sampled, so we expect it to not be linked to + # the ack span + assert len(spans[2].links) == 0 + + assert ack_span.name == "subscriptionID ack" + assert ack_span.kind == trace.SpanKind.CLIENT + assert ack_span.parent is None + assert len(ack_span.links) == 1 + assert ack_span.attributes["messaging.system"] == "gcp_pubsub" + assert ack_span.attributes["messaging.batch.message_count"] == 2 + assert ack_span.attributes["messaging.operation"] == "ack" + assert ack_span.attributes["gcp.project_id"] == "projectID" + assert ack_span.attributes["messaging.destination.name"] == "subscriptionID" + assert ack_span.attributes["code.function"] == "ack" + + def test_ack(): manager = mock.create_autospec( streaming_pull_manager.StreamingPullManager, instance=True @@ -481,6 +606,92 @@ def test_retry_acks_in_new_thread(): assert ctor_call.kwargs["daemon"] +@pytest.mark.skipif( + sys.version_info < (3, 8), + reason="Open Telemetry not supported below Python version 3.8", +) +def test_opentelemetry_retry_acks(span_exporter): + manager = mock.create_autospec( + streaming_pull_manager.StreamingPullManager, instance=True + ) + dispatcher_ = dispatcher.Dispatcher(manager, mock.sentinel.queue) + data1 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo")) + data1.start_subscribe_span( + subscription="projects/projectID/subscriptions/subscriptionID", + exactly_once_enabled=True, + ack_id="ack_id", + delivery_attempt=5, + ) + data2 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo")) + data2.start_subscribe_span( + subscription="projects/projectID/subscriptions/subscriptionID", + exactly_once_enabled=True, + ack_id="ack_id", + delivery_attempt=5, + ) + + f = futures.Future() + items = [ + requests.AckRequest( + ack_id="ack_id_string", + byte_size=0, + time_to_ack=20, + ordering_key="", + future=f, + opentelemetry_data=data1, + ), + requests.AckRequest( + ack_id="ack_id_string2", + byte_size=0, + time_to_ack=20, + ordering_key="", + future=f, + opentelemetry_data=data2, + ), + ] + manager.send_unary_ack.side_effect = [(items, [])] + mock_span_context = mock.Mock(spec=trace.SpanContext) + mock_span_context.trace_flags.sampled = False + with mock.patch("time.sleep", return_value=None): + with mock.patch.object( + data2._subscribe_span, "get_span_context", return_value=mock_span_context + ): + dispatcher_._retry_acks(items) + + spans = span_exporter.get_finished_spans() + + assert len(spans) == 3 + ack_span = spans[0] + + for subscribe_span in spans[1:]: + assert "messaging.gcp_pubsub.result" in subscribe_span.attributes + assert subscribe_span.attributes["messaging.gcp_pubsub.result"] == "acked" + assert len(subscribe_span.events) == 2 + assert subscribe_span.events[0].name == "ack start" + assert subscribe_span.events[1].name == "ack end" + + # This subscribe span is sampled, so we expect it to be linked to the ack + # span. + assert len(spans[1].links) == 1 + assert spans[1].links[0].context == ack_span.context + assert len(spans[1].links[0].attributes) == 1 + assert spans[1].links[0].attributes["messaging.operation.name"] == "ack" + # This subscribe span is not sampled, so we expect it to not be linked to + # the ack span + assert len(spans[2].links) == 0 + + assert ack_span.name == "subscriptionID ack" + assert ack_span.kind == trace.SpanKind.CLIENT + assert ack_span.parent is None + assert len(ack_span.links) == 1 + assert ack_span.attributes["messaging.system"] == "gcp_pubsub" + assert ack_span.attributes["messaging.batch.message_count"] == 2 + assert ack_span.attributes["messaging.operation"] == "ack" + assert ack_span.attributes["gcp.project_id"] == "projectID" + assert ack_span.attributes["messaging.destination.name"] == "subscriptionID" + assert ack_span.attributes["code.function"] == "ack" + + def test_retry_acks(): manager = mock.create_autospec( streaming_pull_manager.StreamingPullManager, instance=True @@ -544,6 +755,125 @@ def test_retry_modacks_in_new_thread(): assert ctor_call.kwargs["daemon"] +def test_opentelemetry_retry_modacks(span_exporter): + manager = mock.create_autospec( + streaming_pull_manager.StreamingPullManager, instance=True + ) + dispatcher_ = dispatcher.Dispatcher(manager, mock.sentinel.queue) + + opentelemetry_data = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo")) + opentelemetry_data.start_subscribe_span( + subscription="projects/projectID/subscriptions/subscriptionID", + exactly_once_enabled=True, + ack_id="ack_id", + delivery_attempt=5, + ) + + f = futures.Future() + items = [ + requests.ModAckRequest( + ack_id="ack_id_string", + seconds=20, + future=f, + opentelemetry_data=opentelemetry_data, + ) + ] + manager.send_unary_modack.side_effect = [(items, [])] + with mock.patch("time.sleep", return_value=None): + dispatcher_._retry_modacks(items) + + # Subscribe span wouldn't be ended for modacks. So, end it in the test, so + # that we can export and assert its contents. + opentelemetry_data.end_subscribe_span() + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + subscribe_span = spans[0] + + assert len(subscribe_span.events) == 1 + assert subscribe_span.events[0].name == "modack end" + + +@pytest.mark.skipif( + sys.version_info < (3, 8), + reason="Open Telemetry not supported below Python version 3.8", +) +def test_opentelemetry_retry_nacks(span_exporter): + manager = mock.create_autospec( + streaming_pull_manager.StreamingPullManager, instance=True + ) + dispatcher_ = dispatcher.Dispatcher(manager, mock.sentinel.queue) + + data1 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo")) + data1.start_subscribe_span( + subscription="projects/projectID/subscriptions/subscriptionID", + exactly_once_enabled=True, + ack_id="ack_id1", + delivery_attempt=5, + ) + data2 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo")) + data2.start_subscribe_span( + subscription="projects/projectID/subscriptions/subscriptionID", + exactly_once_enabled=True, + ack_id="ack_id2", + delivery_attempt=5, + ) + + f = futures.Future() + items = [ + requests.ModAckRequest( + ack_id="ack_id1", + seconds=0, + future=f, + opentelemetry_data=data1, + ), + requests.ModAckRequest( + ack_id="ack_id2", + seconds=0, + future=f, + opentelemetry_data=data2, + ), + ] + manager.send_unary_modack.side_effect = [(items, [])] + mock_span_context = mock.Mock(spec=trace.SpanContext) + mock_span_context.trace_flags.sampled = False + with mock.patch("time.sleep", return_value=None): + with mock.patch.object( + data2._subscribe_span, "get_span_context", return_value=mock_span_context + ): + dispatcher_._retry_modacks(items) + + spans = span_exporter.get_finished_spans() + assert len(spans) == 3 + nack_span = spans[0] + + for subscribe_span in spans[1:]: + assert "messaging.gcp_pubsub.result" in subscribe_span.attributes + assert subscribe_span.attributes["messaging.gcp_pubsub.result"] == "nacked" + assert len(subscribe_span.events) == 1 + assert subscribe_span.events[0].name == "nack end" + + # This subscribe span is sampled, so we expect it to be linked to the nack + # span. + assert len(spans[1].links) == 1 + assert spans[1].links[0].context == nack_span.context + assert len(spans[1].links[0].attributes) == 1 + assert spans[1].links[0].attributes["messaging.operation.name"] == "nack" + # This subscribe span is not sampled, so we expect it to not be linked to + # the nack span + assert len(spans[2].links) == 0 + + assert nack_span.name == "subscriptionID nack" + assert nack_span.kind == trace.SpanKind.CLIENT + assert nack_span.parent is None + assert len(nack_span.links) == 1 + assert nack_span.attributes["messaging.system"] == "gcp_pubsub" + assert nack_span.attributes["messaging.batch.message_count"] == 2 + assert nack_span.attributes["messaging.operation"] == "nack" + assert nack_span.attributes["gcp.project_id"] == "projectID" + assert nack_span.attributes["messaging.destination.name"] == "subscriptionID" + assert nack_span.attributes["code.function"] == "modify_ack_deadline" + + def test_retry_modacks(): manager = mock.create_autospec( streaming_pull_manager.StreamingPullManager, instance=True @@ -633,6 +963,103 @@ def test_drop_ordered_messages(): manager.maybe_resume_consumer.assert_called_once() +@pytest.mark.skipif( + sys.version_info < (3, 8), + reason="Open Telemetry not supported below Python version 3.8", +) +def test_opentelemetry_nack(span_exporter): + manager = mock.create_autospec( + streaming_pull_manager.StreamingPullManager, instance=True + ) + dispatcher_ = dispatcher.Dispatcher(manager, mock.sentinel.queue) + + data1 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo")) + data1.start_subscribe_span( + subscription="projects/projectID/subscriptions/subscriptionID", + exactly_once_enabled=True, + ack_id="ack_id", + delivery_attempt=5, + ) + data2 = SubscribeOpenTelemetry(message=PubsubMessage(data=b"foo")) + data2.start_subscribe_span( + subscription="projects/projectID/subscriptions/subscriptionID", + exactly_once_enabled=True, + ack_id="ack_id2", + delivery_attempt=5, + ) + + items = [ + requests.NackRequest( + ack_id="ack_id", + byte_size=10, + ordering_key="", + future=None, + opentelemetry_data=data1, + ), + requests.NackRequest( + ack_id="ack_id2", + byte_size=10, + ordering_key="", + future=None, + opentelemetry_data=data2, + ), + ] + response_items = [ + requests.ModAckRequest( + ack_id="ack_id", + seconds=0, + future=None, + opentelemetry_data=data1, + ), + requests.ModAckRequest( + ack_id="ack_id2", + seconds=0, + future=None, + opentelemetry_data=data2, + ), + ] + manager.send_unary_modack.return_value = (response_items, []) + + mock_span_context = mock.Mock(spec=trace.SpanContext) + mock_span_context.trace_flags.sampled = False + with mock.patch.object( + data2._subscribe_span, "get_span_context", return_value=mock_span_context + ): + dispatcher_.nack(items) + + spans = span_exporter.get_finished_spans() + + assert len(spans) == 3 + nack_span = spans[0] + for subscribe_span in spans[1:]: + assert "messaging.gcp_pubsub.result" in subscribe_span.attributes + assert subscribe_span.attributes["messaging.gcp_pubsub.result"] == "nacked" + assert len(subscribe_span.events) == 2 + assert subscribe_span.events[0].name == "nack start" + assert subscribe_span.events[1].name == "nack end" + + # This subscribe span is sampled, so we expect it to be linked to the nack + # span. + assert len(spans[1].links) == 1 + assert spans[1].links[0].context == nack_span.context + assert len(spans[1].links[0].attributes) == 1 + assert spans[1].links[0].attributes["messaging.operation.name"] == "nack" + # This subscribe span is not sampled, so we expect it to not be linked to + # the nack span + assert len(spans[2].links) == 0 + + assert nack_span.name == "subscriptionID nack" + assert nack_span.kind == trace.SpanKind.CLIENT + assert nack_span.parent is None + assert len(nack_span.links) == 1 + assert nack_span.attributes["messaging.system"] == "gcp_pubsub" + assert nack_span.attributes["messaging.batch.message_count"] == 2 + assert nack_span.attributes["messaging.operation"] == "nack" + assert nack_span.attributes["gcp.project_id"] == "projectID" + assert nack_span.attributes["messaging.destination.name"] == "subscriptionID" + assert nack_span.attributes["code.function"] == "modify_ack_deadline" + + def test_nack(): manager = mock.create_autospec( streaming_pull_manager.StreamingPullManager, instance=True diff --git a/tests/unit/pubsub_v1/subscriber/test_leaser.py b/tests/unit/pubsub_v1/subscriber/test_leaser.py index f38717c6f..b5b5cac20 100644 --- a/tests/unit/pubsub_v1/subscriber/test_leaser.py +++ b/tests/unit/pubsub_v1/subscriber/test_leaser.py @@ -22,6 +22,10 @@ from google.cloud.pubsub_v1.subscriber._protocol import leaser from google.cloud.pubsub_v1.subscriber._protocol import requests from google.cloud.pubsub_v1.subscriber._protocol import streaming_pull_manager +from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import ( + SubscribeOpenTelemetry, +) +from google.cloud.pubsub_v1.subscriber import message # special case python < 3.8 if sys.version_info.major == 3 and sys.version_info.minor < 8: @@ -136,6 +140,101 @@ def trigger_done(timeout): leaser._stop_event.wait = trigger_done +def test_opentelemetry_dropped_message_process_span(span_exporter): + manager = create_manager() + leaser_ = leaser.Leaser(manager) + make_sleep_mark_event_as_done(leaser_) + msg = mock.create_autospec( + message.Message, instance=True, ack_id="ack_foo", size=10 + ) + msg.message_id = 3 + opentelemetry_data = SubscribeOpenTelemetry(msg) + opentelemetry_data.start_subscribe_span( + subscription="projects/projectId/subscriptions/subscriptionID", + exactly_once_enabled=False, + ack_id="ack_id", + delivery_attempt=4, + ) + opentelemetry_data.start_process_span() + leaser_.add( + [ + requests.LeaseRequest( + ack_id="my ack id", + byte_size=50, + ordering_key="", + opentelemetry_data=opentelemetry_data, + ) + ] + ) + leased_messages_dict = leaser_._leased_messages + + # Setting the `sent_time`` to be less than `cutoff` in order to make the leased message expire. + # This will exercise the code path where the message would be dropped from the leaser + leased_messages_dict["my ack id"] = leased_messages_dict["my ack id"]._replace( + sent_time=0 + ) + + manager._send_lease_modacks.return_value = set() + leaser_.maintain_leases() + + opentelemetry_data.end_subscribe_span() + spans = span_exporter.get_finished_spans() + assert len(spans) == 2 + process_span, subscribe_span = spans + + assert process_span.name == "subscriptionID process" + assert subscribe_span.name == "subscriptionID subscribe" + + assert len(process_span.events) == 1 + assert process_span.events[0].name == "expired" + + assert process_span.parent == subscribe_span.context + + +def test_opentelemetry_expired_message_exactly_once_process_span(span_exporter): + manager = create_manager() + leaser_ = leaser.Leaser(manager) + make_sleep_mark_event_as_done(leaser_) + msg = mock.create_autospec( + message.Message, instance=True, ack_id="ack_foo", size=10 + ) + msg.message_id = 3 + opentelemetry_data = SubscribeOpenTelemetry(msg) + opentelemetry_data.start_subscribe_span( + subscription="projects/projectId/subscriptions/subscriptionID", + exactly_once_enabled=True, + ack_id="ack_id", + delivery_attempt=4, + ) + opentelemetry_data.start_process_span() + leaser_.add( + [ + requests.LeaseRequest( + ack_id="my ack id", + byte_size=50, + ordering_key="", + opentelemetry_data=opentelemetry_data, + ) + ] + ) + + manager._send_lease_modacks.return_value = ["my ack id"] + leaser_.maintain_leases() + + opentelemetry_data.end_subscribe_span() + spans = span_exporter.get_finished_spans() + assert len(spans) == 2 + process_span, subscribe_span = spans + + assert process_span.name == "subscriptionID process" + assert subscribe_span.name == "subscriptionID subscribe" + + assert len(process_span.events) == 1 + assert process_span.events[0].name == "expired" + + assert process_span.parent == subscribe_span.context + + def test_maintain_leases_ack_ids(): manager = create_manager() leaser_ = leaser.Leaser(manager) diff --git a/tests/unit/pubsub_v1/subscriber/test_message.py b/tests/unit/pubsub_v1/subscriber/test_message.py index 49b07b7fd..8d9d2566e 100644 --- a/tests/unit/pubsub_v1/subscriber/test_message.py +++ b/tests/unit/pubsub_v1/subscriber/test_message.py @@ -29,6 +29,9 @@ from google.protobuf import timestamp_pb2 from google.pubsub_v1 import types as gapic_types from google.cloud.pubsub_v1.subscriber.exceptions import AcknowledgeStatus +from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import ( + SubscribeOpenTelemetry, +) RECEIVED = datetime.datetime(2012, 4, 21, 15, 0, tzinfo=datetime.timezone.utc) @@ -131,6 +134,155 @@ def check_call_types(mock, *args, **kwargs): assert isinstance(call_args[n], argtype) +def test_opentelemetry_ack(span_exporter): + SUBSCRIPTION = "projects/projectID/subscriptions/subscriptionID" + msg = create_message(b"data", ack_id="ack_id") + opentelemetry_data = SubscribeOpenTelemetry(msg) + opentelemetry_data.start_subscribe_span( + subscription=SUBSCRIPTION, + exactly_once_enabled=False, + ack_id="ack_id", + delivery_attempt=2, + ) + opentelemetry_data.start_process_span() + msg.opentelemetry_data = opentelemetry_data + msg.ack() + opentelemetry_data.end_subscribe_span() + + spans = span_exporter.get_finished_spans() + assert len(spans) == 2 + process_span, subscribe_span = spans + + assert subscribe_span.name == "subscriptionID subscribe" + assert len(subscribe_span.events) == 0 + + assert process_span.name == "subscriptionID process" + assert len(process_span.events) == 1 + assert process_span.events[0].name == "ack called" + + +def test_opentelemetry_ack_with_response(span_exporter): + SUBSCRIPTION = "projects/projectID/subscriptions/subscriptionID" + msg = create_message(b"data", ack_id="ack_id") + opentelemetry_data = SubscribeOpenTelemetry(msg) + opentelemetry_data.start_subscribe_span( + subscription=SUBSCRIPTION, + exactly_once_enabled=False, + ack_id="ack_id", + delivery_attempt=2, + ) + opentelemetry_data.start_process_span() + msg.opentelemetry_data = opentelemetry_data + msg.ack_with_response() + opentelemetry_data.end_subscribe_span() + + spans = span_exporter.get_finished_spans() + assert len(spans) == 2 + process_span, subscribe_span = spans + + assert subscribe_span.name == "subscriptionID subscribe" + assert len(subscribe_span.events) == 0 + + assert process_span.name == "subscriptionID process" + assert len(process_span.events) == 1 + assert process_span.events[0].name == "ack called" + + +def test_opentelemetry_nack(span_exporter): + SUBSCRIPTION = "projects/projectID/subscriptions/subscriptionID" + msg = create_message(b"data", ack_id="ack_id") + opentelemetry_data = SubscribeOpenTelemetry(msg) + opentelemetry_data.start_subscribe_span( + subscription=SUBSCRIPTION, + exactly_once_enabled=False, + ack_id="ack_id", + delivery_attempt=2, + ) + opentelemetry_data.start_process_span() + msg.opentelemetry_data = opentelemetry_data + msg.nack() + opentelemetry_data.end_subscribe_span() + + spans = span_exporter.get_finished_spans() + assert len(spans) == 2 + process_span, subscribe_span = spans + + assert subscribe_span.name == "subscriptionID subscribe" + assert len(subscribe_span.events) == 0 + + assert process_span.name == "subscriptionID process" + assert len(process_span.events) == 1 + assert process_span.events[0].name == "nack called" + + +def test_opentelemetry_nack_with_response(span_exporter): + SUBSCRIPTION = "projects/projectID/subscriptions/subscriptionID" + msg = create_message(b"data", ack_id="ack_id") + opentelemetry_data = SubscribeOpenTelemetry(msg) + opentelemetry_data.start_subscribe_span( + subscription=SUBSCRIPTION, + exactly_once_enabled=False, + ack_id="ack_id", + delivery_attempt=2, + ) + opentelemetry_data.start_process_span() + msg.opentelemetry_data = opentelemetry_data + msg.nack_with_response() + opentelemetry_data.end_subscribe_span() + + spans = span_exporter.get_finished_spans() + assert len(spans) == 2 + + process_span, subscribe_span = spans + + assert subscribe_span.name == "subscriptionID subscribe" + assert len(subscribe_span.events) == 0 + + assert process_span.name == "subscriptionID process" + assert len(process_span.events) == 1 + assert process_span.events[0].name == "nack called" + + +def test_opentelemetry_modack(span_exporter): + SUBSCRIPTION = "projects/projectID/subscriptions/subscriptionID" + msg = create_message(b"data", ack_id="ack_id") + opentelemetry_data = SubscribeOpenTelemetry(msg) + opentelemetry_data.start_subscribe_span( + subscription=SUBSCRIPTION, + exactly_once_enabled=False, + ack_id="ack_id", + delivery_attempt=2, + ) + msg.opentelemetry_data = opentelemetry_data + msg.modify_ack_deadline(3) + opentelemetry_data.end_subscribe_span() + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + + assert len(spans[0].events) == 0 + + +def test_opentelemetry_modack_with_response(span_exporter): + SUBSCRIPTION = "projects/projectID/subscriptions/subscriptionID" + msg = create_message(b"data", ack_id="ack_id") + opentelemetry_data = SubscribeOpenTelemetry(msg) + opentelemetry_data.start_subscribe_span( + subscription=SUBSCRIPTION, + exactly_once_enabled=False, + ack_id="ack_id", + delivery_attempt=2, + ) + msg.opentelemetry_data = opentelemetry_data + msg.modify_ack_deadline_with_response(3) + opentelemetry_data.end_subscribe_span() + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + + assert len(spans[0].events) == 0 + + def test_ack(): msg = create_message(b"foo", ack_id="bogus_ack_id") with mock.patch.object(msg._request_queue, "put") as put: diff --git a/tests/unit/pubsub_v1/subscriber/test_messages_on_hold.py b/tests/unit/pubsub_v1/subscriber/test_messages_on_hold.py index 5e1dcf91b..64963de48 100644 --- a/tests/unit/pubsub_v1/subscriber/test_messages_on_hold.py +++ b/tests/unit/pubsub_v1/subscriber/test_messages_on_hold.py @@ -14,9 +14,14 @@ import queue +from opentelemetry import trace + +from google.pubsub_v1 import types as gapic_types from google.cloud.pubsub_v1.subscriber import message from google.cloud.pubsub_v1.subscriber._protocol import messages_on_hold -from google.pubsub_v1 import types as gapic_types +from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import ( + SubscribeOpenTelemetry, +) def make_message(ack_id, ordering_key): @@ -37,6 +42,32 @@ def test_init(): assert moh.get() is None +def test_opentelemetry_subscriber_scheduler_span(span_exporter): + moh = messages_on_hold.MessagesOnHold() + msg = make_message(ack_id="ack1", ordering_key="") + opentelemetry_data = SubscribeOpenTelemetry(msg) + msg.opentelemetry_data = opentelemetry_data + opentelemetry_data.start_subscribe_span( + subscription="projects/projectId/subscriptions/subscriptionID", + exactly_once_enabled=False, + ack_id="ack_id", + delivery_attempt=4, + ) + moh.put(msg) + opentelemetry_data.end_subscribe_scheduler_span() + opentelemetry_data.end_subscribe_span() + + spans = span_exporter.get_finished_spans() + + assert len(spans) == 2 + + subscribe_scheduler_span, subscribe_span = spans + + assert subscribe_scheduler_span.name == "subscriber scheduler" + assert subscribe_scheduler_span.kind == trace.SpanKind.INTERNAL + assert subscribe_scheduler_span.parent == subscribe_span.context + + def test_put_and_get_unordered_messages(): moh = messages_on_hold.MessagesOnHold() diff --git a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py index d4ce2cfdb..4d2d1b98e 100644 --- a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py +++ b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py @@ -18,6 +18,20 @@ import threading import time import types as stdlib_types +import datetime +import queue +import math + +from opentelemetry import trace +from google.protobuf import timestamp_pb2 +from google.api_core import datetime_helpers + +from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import ( + SubscribeOpenTelemetry, +) +from google.cloud.pubsub_v1.subscriber.message import Message +from google.cloud.pubsub_v1.types import PubsubMessage + # special case python < 3.8 if sys.version_info.major == 3 and sys.version_info.minor < 8: @@ -179,11 +193,16 @@ def test_constructor_with_max_duration_per_lease_extension_too_high(): assert manager._stream_ack_deadline == 600 -def make_manager(**kwargs): +def make_manager( + enable_open_telemetry: bool = False, + subscription_name: str = "subscription-name", + **kwargs, +): client_ = mock.create_autospec(client.Client, instance=True) + client_.open_telemetry_enabled = enable_open_telemetry scheduler_ = mock.create_autospec(scheduler.Scheduler, instance=True) return streaming_pull_manager.StreamingPullManager( - client_, "subscription-name", scheduler=scheduler_, **kwargs + client_, subscription_name, scheduler=scheduler_, **kwargs ) @@ -509,6 +528,45 @@ def test__maybe_release_messages_on_overload(): manager._scheduler.schedule.assert_not_called() +def test_opentelemetry__maybe_release_messages_subscribe_scheduler_span(span_exporter): + manager = make_manager( + flow_control=types.FlowControl(max_messages=10, max_bytes=1000) + ) + manager._callback = mock.sentinel.callback + + # Init leaser message count to 11, so that when subtracting the 3 messages + # that are on hold, there is still room for another 2 messages before the + # max load is hit. + _leaser = manager._leaser = mock.create_autospec(leaser.Leaser) + fake_leaser_add(_leaser, init_msg_count=8, assumed_msg_size=10) + msg = mock.create_autospec( + message.Message, instance=True, ack_id="ack_foo", size=10 + ) + msg.message_id = 3 + opentelemetry_data = SubscribeOpenTelemetry(msg) + msg.opentelemetry_data = opentelemetry_data + opentelemetry_data.start_subscribe_span( + subscription="projects/projectId/subscriptions/subscriptionID", + exactly_once_enabled=False, + ack_id="ack_id", + delivery_attempt=4, + ) + manager._messages_on_hold.put(msg) + manager._maybe_release_messages() + opentelemetry_data.end_subscribe_span() + spans = span_exporter.get_finished_spans() + + assert len(spans) == 2 + + subscriber_scheduler_span, subscribe_span = spans + + assert subscriber_scheduler_span.name == "subscriber scheduler" + assert subscribe_span.name == "subscriptionID subscribe" + + assert subscriber_scheduler_span.parent == subscribe_span.context + assert subscriber_scheduler_span.kind == trace.SpanKind.INTERNAL + + def test__maybe_release_messages_below_overload(): manager = make_manager( flow_control=types.FlowControl(max_messages=10, max_bytes=1000) @@ -574,6 +632,86 @@ def test__maybe_release_messages_negative_on_hold_bytes_warning(caplog): assert manager._on_hold_bytes == 0 # should be auto-corrected +@pytest.mark.skipif( + sys.version_info < (3, 8), + reason="Open Telemetry not supported below Python version 3.8", +) +@pytest.mark.parametrize( + "receipt_modack", + [ + True, + False, + ], +) +def test_opentelemetry__send_lease_modacks(span_exporter, receipt_modack): + manager, _, _, _, _, _ = make_running_manager( + enable_open_telemetry=True, + subscription_name="projects/projectID/subscriptions/subscriptionID", + ) + data1 = SubscribeOpenTelemetry( + message=gapic_types.PubsubMessage(data=b"foo", message_id="1") + ) + data2 = SubscribeOpenTelemetry( + message=gapic_types.PubsubMessage(data=b"bar", message_id="2") + ) + + data1.start_subscribe_span( + subscription="projects/projectID/subscriptions/subscriptionID", + exactly_once_enabled=False, + ack_id="ack_id1", + delivery_attempt=2, + ) + data2.start_subscribe_span( + subscription="projects/projectID/subscriptions/subscriptionID", + exactly_once_enabled=True, + ack_id="ack_id1", + delivery_attempt=2, + ) + mock_span_context = mock.Mock(spec=trace.SpanContext) + mock_span_context.trace_flags.sampled = False + with mock.patch.object( + data1._subscribe_span, "get_span_context", return_value=mock_span_context + ): + manager._send_lease_modacks( + ack_ids=["ack_id1", "ack_id2"], + ack_deadline=20, + opentelemetry_data=[data1, data2], + receipt_modack=receipt_modack, + ) + data1.end_subscribe_span() + data2.end_subscribe_span() + spans = span_exporter.get_finished_spans() + assert len(spans) == 3 + modack_span, subscribe_span1, subscribe_span2 = spans + + assert len(subscribe_span1.events) == 0 + assert len(subscribe_span2.events) == 0 + + assert len(subscribe_span1.links) == 0 + assert len(subscribe_span2.links) == 1 + assert subscribe_span2.links[0].context == modack_span.context + assert subscribe_span2.links[0].attributes["messaging.operation.name"] == "modack" + + assert modack_span.name == "subscriptionID modack" + assert modack_span.parent is None + assert modack_span.kind == trace.SpanKind.CLIENT + assert len(modack_span.links) == 1 + modack_span_attributes = modack_span.attributes + assert modack_span_attributes["messaging.system"] == "gcp_pubsub" + assert modack_span_attributes["messaging.batch.message_count"] == 2 + assert math.isclose( + modack_span_attributes["messaging.gcp_pubsub.message.ack_deadline"], 20 + ) + assert modack_span_attributes["messaging.destination.name"] == "subscriptionID" + assert modack_span_attributes["gcp.project_id"] == "projectID" + assert modack_span_attributes["messaging.operation.name"] == "modack" + assert modack_span_attributes["code.function"] == "_send_lease_modacks" + assert ( + modack_span_attributes["messaging.gcp_pubsub.is_receipt_modack"] + == receipt_modack + ) + + def test_send_unary_ack(): manager = make_manager() @@ -1224,14 +1362,17 @@ def test_open_has_been_closed(): manager.open(mock.sentinel.callback, mock.sentinel.on_callback_error) -def make_running_manager(**kwargs): - manager = make_manager(**kwargs) +def make_running_manager( + enable_open_telemetry: bool = False, + subscription_name: str = "subscription-name", + **kwargs, +): + manager = make_manager(enable_open_telemetry, subscription_name, **kwargs) manager._consumer = mock.create_autospec(bidi.BackgroundConsumer, instance=True) manager._consumer.is_active = True manager._dispatcher = mock.create_autospec(dispatcher.Dispatcher, instance=True) manager._leaser = mock.create_autospec(leaser.Leaser, instance=True) manager._heartbeater = mock.create_autospec(heartbeater.Heartbeater, instance=True) - return ( manager, manager._consumer, @@ -2626,3 +2767,148 @@ def test_process_requests_mixed_success_and_failure_modacks(): # message with ack_id 'ackid3' succeeds assert requests_completed[1].ack_id == "ackid3" assert future3.result() == subscriber_exceptions.AcknowledgeStatus.SUCCESS + + +@pytest.mark.skipif( + sys.version_info < (3, 8), + reason="Open Telemetry not supported below Python version 3.8", +) +def test_opentelemetry__on_response_subscribe_span_create(span_exporter): + manager, _, _, leaser, _, _ = make_running_manager( + enable_open_telemetry=True, + subscription_name="projects/projectID/subscriptions/subscriptionID", + ) + + fake_leaser_add(leaser, init_msg_count=0, assumed_msg_size=42) + manager._callback = mock.sentinel.callback + + response = gapic_types.StreamingPullResponse( + received_messages=[ + gapic_types.ReceivedMessage( + ack_id="ack1", + message=gapic_types.PubsubMessage(data=b"foo", message_id="1"), + ), + gapic_types.ReceivedMessage( + ack_id="ack2", + message=gapic_types.PubsubMessage(data=b"bar", message_id="2"), + delivery_attempt=6, + ), + ] + ) + + manager._on_response(response) + + spans = span_exporter.get_finished_spans() + + # Subscribe span is still active, hence unexported. + # Subscriber scheduler spans corresponding to the two messages would be started in `messages_on_hold.put()`` + # and ended in `_maybe_release_messages` + assert len(spans) == 3 + modack_span = spans[0] + + for span in spans[1:]: + assert span.name == "subscriber scheduler" + assert span.kind == trace.SpanKind.INTERNAL + assert span.parent is not None + assert len(span.attributes) == 0 + + assert modack_span.name == "subscriptionID modack" + assert modack_span.kind == trace.SpanKind.CLIENT + assert modack_span.parent is None + assert len(modack_span.links) == 2 + + +RECEIVED = datetime.datetime(2012, 4, 21, 15, 0, tzinfo=datetime.timezone.utc) +RECEIVED_SECONDS = datetime_helpers.to_milliseconds(RECEIVED) // 1000 +PUBLISHED_MICROS = 123456 +PUBLISHED = RECEIVED + datetime.timedelta(days=1, microseconds=PUBLISHED_MICROS) +PUBLISHED_SECONDS = datetime_helpers.to_milliseconds(PUBLISHED) // 1000 + + +def create_message( + data, + ack_id="ACKID", + delivery_attempt=0, + ordering_key="", + exactly_once_delivery_enabled=False, + **attrs, +): # pragma: NO COVER + with mock.patch.object(time, "time") as time_: + time_.return_value = RECEIVED_SECONDS + gapic_pubsub_message = PubsubMessage( + attributes=attrs, + data=data, + message_id="message_id", + publish_time=timestamp_pb2.Timestamp( + seconds=PUBLISHED_SECONDS, nanos=PUBLISHED_MICROS * 1000 + ), + ordering_key=ordering_key, + ) + msg = Message( + # The code under test uses a raw protobuf PubsubMessage, i.e. w/o additional + # Python class wrappers, hence the "_pb" + message=gapic_pubsub_message._pb, + ack_id=ack_id, + delivery_attempt=delivery_attempt, + request_queue=queue.Queue(), + exactly_once_delivery_enabled_func=lambda: exactly_once_delivery_enabled, + ) + return msg + + +def test_opentelemetry_subscriber_concurrency_control_span(span_exporter): + manager, _, _, leaser, _, _ = make_running_manager( + enable_open_telemetry=True, + subscription_name="projects/projectID/subscriptions/subscriptionID", + ) + manager._callback = mock.Mock() + msg = create_message(b"foo") + opentelemetry_data = SubscribeOpenTelemetry(msg) + opentelemetry_data.start_subscribe_span( + subscription="projects/projectId/subscriptions/subscriptionID", + exactly_once_enabled=False, + ack_id="ack_id", + delivery_attempt=4, + ) + msg.opentelemetry_data = opentelemetry_data + manager._schedule_message_on_hold(msg) + opentelemetry_data.end_subscribe_concurrency_control_span() + opentelemetry_data.end_subscribe_span() + + spans = span_exporter.get_finished_spans() + assert len(spans) == 2 + + concurrency_control_span, subscribe_span = spans + assert concurrency_control_span.name == "subscriber concurrency control" + assert subscribe_span.name == "subscriptionID subscribe" + assert opentelemetry_data.subscription_id == "subscriptionID" + + assert concurrency_control_span.parent == subscribe_span.context + + +def test_opentelemetry_subscriber_concurrency_control_span_end(span_exporter): + msg = create_message(b"foo") + opentelemetry_data = SubscribeOpenTelemetry(msg) + opentelemetry_data.start_subscribe_span( + subscription="projects/projectId/subscriptions/subscriptionID", + exactly_once_enabled=False, + ack_id="ack_id", + delivery_attempt=4, + ) + opentelemetry_data.start_subscribe_concurrency_control_span() + msg.opentelemetry_data = opentelemetry_data + streaming_pull_manager._wrap_callback_errors(mock.Mock(), mock.Mock(), msg) + + spans = span_exporter.get_finished_spans() + assert len(spans) == 1 + + concurrency_control_span = spans[0] + concurrency_control_span.name == "subscriber concurrency control" + + +def test_opentelemetry_wrap_callback_error(span_exporter): + msg = create_message(b"foo") + streaming_pull_manager._wrap_callback_errors(mock.Mock(), mock.Mock(), msg) + + spans = span_exporter.get_finished_spans() + assert len(spans) == 0 diff --git a/tests/unit/pubsub_v1/subscriber/test_subscribe_opentelemetry.py b/tests/unit/pubsub_v1/subscriber/test_subscribe_opentelemetry.py new file mode 100644 index 000000000..2fb89aa7c --- /dev/null +++ b/tests/unit/pubsub_v1/subscriber/test_subscribe_opentelemetry.py @@ -0,0 +1,202 @@ +# Copyright 2024, Google LLC All rights reserved. +# +# 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 datetime +import time +import sys +import queue +import pytest + +from google.protobuf import timestamp_pb2 +from google.api_core import datetime_helpers +from opentelemetry import trace +from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator +from google.cloud.pubsub_v1.open_telemetry.context_propagation import ( + OpenTelemetryContextSetter, +) + +from google.cloud.pubsub_v1.open_telemetry.subscribe_opentelemetry import ( + SubscribeOpenTelemetry, +) +from google.cloud.pubsub_v1.subscriber.message import Message +from google.cloud.pubsub_v1.types import PubsubMessage + +# special case python < 3.8 +if sys.version_info.major == 3 and sys.version_info.minor < 8: + import mock +else: + from unittest import mock + +RECEIVED = datetime.datetime(2012, 4, 21, 15, 0, tzinfo=datetime.timezone.utc) +RECEIVED_SECONDS = datetime_helpers.to_milliseconds(RECEIVED) // 1000 +PUBLISHED_MICROS = 123456 +PUBLISHED = RECEIVED + datetime.timedelta(days=1, microseconds=PUBLISHED_MICROS) +PUBLISHED_SECONDS = datetime_helpers.to_milliseconds(PUBLISHED) // 1000 + + +def create_message( + data, + ack_id="ACKID", + delivery_attempt=0, + ordering_key="", + exactly_once_delivery_enabled=False, + **attrs +): # pragma: NO COVER + with mock.patch.object(time, "time") as time_: + time_.return_value = RECEIVED_SECONDS + gapic_pubsub_message = PubsubMessage( + attributes=attrs, + data=data, + message_id="message_id", + publish_time=timestamp_pb2.Timestamp( + seconds=PUBLISHED_SECONDS, nanos=PUBLISHED_MICROS * 1000 + ), + ordering_key=ordering_key, + ) + msg = Message( + # The code under test uses a raw protobuf PubsubMessage, i.e. w/o additional + # Python class wrappers, hence the "_pb" + message=gapic_pubsub_message._pb, + ack_id=ack_id, + delivery_attempt=delivery_attempt, + request_queue=queue.Queue(), + exactly_once_delivery_enabled_func=lambda: exactly_once_delivery_enabled, + ) + return msg + + +def test_opentelemetry_set_subscribe_span_result(span_exporter): + msg = create_message(b"foo") + opentelemetry_data = SubscribeOpenTelemetry(msg) + opentelemetry_data.start_subscribe_span( + subscription="projects/projectId/subscriptions/subscriptionID", + exactly_once_enabled=False, + ack_id="ack_id", + delivery_attempt=4, + ) + msg.opentelemetry_data = opentelemetry_data + opentelemetry_data.set_subscribe_span_result("acked") + opentelemetry_data.end_subscribe_span() + spans = span_exporter.get_finished_spans() + + assert len(spans) == 1 + + assert "messaging.gcp_pubsub.result" in spans[0].attributes + assert spans[0].attributes["messaging.gcp_pubsub.result"] == "acked" + + +def test_opentelemetry_set_subscribe_span_result_assert_error(): + msg = create_message(b"foo") + opentelemetry_data = SubscribeOpenTelemetry(msg) + with pytest.raises(AssertionError): + opentelemetry_data.set_subscribe_span_result("hi") + + +def test_opentelemetry_start_subscribe_concurrency_control_span_no_subscribe_span(): + msg = create_message(b"foo") + opentelemetry_data = SubscribeOpenTelemetry(msg) + with pytest.raises(AssertionError): + opentelemetry_data.start_subscribe_concurrency_control_span() + + +def test_opentelemetry_end_subscribe_concurrency_control_span_assertion_error(): + msg = create_message(b"foo") + opentelemetry_data = SubscribeOpenTelemetry(msg) + with pytest.raises(AssertionError): + opentelemetry_data.end_subscribe_concurrency_control_span() + + +def test_opentelemetry_start_subscribe_scheduler_span_assertion_error(): + msg = create_message(b"foo") + opentelemetry_data = SubscribeOpenTelemetry(msg) + with pytest.raises(AssertionError): + opentelemetry_data.start_subscribe_scheduler_span() + + +def test_opentelemetry_end_subscribe_scheduler_span_assertion_error(): + msg = create_message(b"foo") + opentelemetry_data = SubscribeOpenTelemetry(msg) + with pytest.raises(AssertionError): + opentelemetry_data.end_subscribe_scheduler_span() + + +def test_opentelemetry_start_process_span_assertion_error(): + msg = create_message(b"foo") + opentelemetry_data = SubscribeOpenTelemetry(msg) + with pytest.raises(AssertionError): + opentelemetry_data.start_process_span() + + +def test_opentelemetry_end_process_span_assertion_error(): + msg = create_message(b"foo") + opentelemetry_data = SubscribeOpenTelemetry(msg) + with pytest.raises(AssertionError): + opentelemetry_data.end_process_span() + + +def test_opentelemetry_start_process_span_publisher_link(): + msg = create_message(b"foo") + opentelemetry_data = SubscribeOpenTelemetry(msg) + msg.opentelemetry_data = opentelemetry_data + tracer = trace.get_tracer("foo") + publisher_create_span = None + with tracer.start_as_current_span(name="name") as span: + publisher_create_span = span + TraceContextTextMapPropagator().inject( + carrier=msg._message, + setter=OpenTelemetryContextSetter(), + ) + opentelemetry_data.start_subscribe_span( + subscription="projects/projectId/subscriptions/subscriptionID", + exactly_once_enabled=False, + ack_id="ack_id", + delivery_attempt=4, + ) + opentelemetry_data.start_process_span() + assert len(opentelemetry_data._process_span.links) == 1 + assert ( + opentelemetry_data._process_span.links[0].context.span_id + == publisher_create_span.get_span_context().span_id + ) + + +def test_opentelemetry_start_process_span_no_publisher_span(): + msg = create_message(b"foo") + opentelemetry_data = SubscribeOpenTelemetry(msg) + msg.opentelemetry_data = opentelemetry_data + opentelemetry_data.start_subscribe_span( + subscription="projects/projectId/subscriptions/subscriptionID", + exactly_once_enabled=False, + ack_id="ack_id", + delivery_attempt=4, + ) + opentelemetry_data.start_process_span() + # Assert that when no context is propagated, the subscriber span has no parent. + assert opentelemetry_data._subscribe_span.parent is None + # Assert that when there is no publisher create span context propagated, + # There are no links created in the process span. + assert len(opentelemetry_data._process_span.links) == 0 + + +def test_opentelemetry_project_id_set_after_create_subscribe_span(): + msg = create_message(b"foo") + opentelemetry_data = SubscribeOpenTelemetry(msg) + msg.opentelemetry_data = opentelemetry_data + opentelemetry_data.start_subscribe_span( + subscription="projects/projectId/subscriptions/subscriptionID", + exactly_once_enabled=False, + ack_id="ack_id", + delivery_attempt=4, + ) + assert opentelemetry_data.project_id == "projectId" diff --git a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py index 16a6150af..7c0ebfd83 100644 --- a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py +++ b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py @@ -30,6 +30,10 @@ from google.cloud.pubsub_v1.subscriber import futures from google.pubsub_v1.services.subscriber import client as subscriber_client from google.pubsub_v1.services.subscriber.transports.grpc import SubscriberGrpcTransport +from google.cloud.pubsub_v1.open_telemetry.context_propagation import ( + OpenTelemetryContextGetter, +) +from google.pubsub_v1.types import PubsubMessage def test_init_default_client_info(creds): @@ -317,3 +321,48 @@ async def test_sync_pull_warning_if_return_immediately_async(creds): warning_msg = str(warned[0].message) assert "return_immediately" in warning_msg assert "deprecated" in warning_msg + + +@pytest.mark.parametrize( + "enable_open_telemetry", + [ + True, + False, + ], +) +def test_opentelemetry_subscriber_setting(creds, enable_open_telemetry): + options = types.SubscriberOptions( + enable_open_telemetry_tracing=enable_open_telemetry, + ) + if sys.version_info >= (3, 8) or enable_open_telemetry is False: + client = subscriber.Client(credentials=creds, subscriber_options=options) + assert client.subscriber_options == options + assert client._open_telemetry_enabled == enable_open_telemetry + else: + with pytest.warns( + RuntimeWarning, + match="Open Telemetry for Python version 3.7 or lower is not supported. Disabling Open Telemetry tracing.", + ): + client = subscriber.Client(credentials=creds, subscriber_options=options) + assert client._open_telemetry_enabled is False + + +def test_opentelemetry_propagator_get(): + message = PubsubMessage(data=b"foo") + message.attributes["key1"] = "value1" + message.attributes["googclient_key2"] = "value2" + + assert OpenTelemetryContextGetter().get(message, "key2") == ["value2"] + + assert OpenTelemetryContextGetter().get(message, "key1") is None + + +def test_opentelemetry_propagator_keys(): + message = PubsubMessage(data=b"foo") + message.attributes["key1"] = "value1" + message.attributes["googclient_key2"] = "value2" + + assert sorted(OpenTelemetryContextGetter().keys(message)) == [ + "googclient_key2", + "key1", + ] From c31431a9093b3f7fc38a875a11e3649f840c2116 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2024 21:12:03 -0400 Subject: [PATCH 097/190] chore(main): release 2.24.0 (#1251) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 94d5d8cc4..28ce5d0cc 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.23.1" + ".": "2.24.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 93db31182..447013720 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,19 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.24.0](https://github.com/googleapis/python-pubsub/compare/v2.23.1...v2.24.0) (2024-09-24) + + +### Features + +* Add OpenTelemetry support for Subscribe Side ([#1252](https://github.com/googleapis/python-pubsub/issues/1252)) ([1b6f3d2](https://github.com/googleapis/python-pubsub/commit/1b6f3d284095e138943576de8551df263f73a506)) +* Open Telemetry Publish Side Support ([#1241](https://github.com/googleapis/python-pubsub/issues/1241)) ([bb5f3d1](https://github.com/googleapis/python-pubsub/commit/bb5f3d1a7df2d661cccc336edc8eceb2161c6921)) + + +### Bug Fixes + +* Fix flaky test ([#1254](https://github.com/googleapis/python-pubsub/issues/1254)) ([1ae49de](https://github.com/googleapis/python-pubsub/commit/1ae49de09996a5cf19f592f996c46e0222d540fc)) + ## [2.23.1](https://github.com/googleapis/python-pubsub/compare/v2.23.0...v2.23.1) (2024-09-09) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 008f4dd36..07de09d56 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.23.1" # {x-release-please-version} +__version__ = "2.24.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 008f4dd36..07de09d56 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.23.1" # {x-release-please-version} +__version__ = "2.24.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index c1602f5ba..fd163b590 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.23.1" + "version": "2.24.0" }, "snippets": [ { From 747a016c6d9936adc0db36584537e6879df5eb93 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:14:46 -0700 Subject: [PATCH 098/190] build(python): release script update (#1253) Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 ++-- .kokoro/release.sh | 2 +- .kokoro/release/common.cfg | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index f8bd8149f..597e0c326 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:365d92ef2206cfad00a8c5955c36789d0de124e2b6d92a72dd0486315a0f2e57 -# created: 2024-09-04T14:50:52.658171431Z + digest: sha256:e8dcfd7cbfd8beac3a3ff8d3f3185287ea0625d859168cc80faccfc9a7a00455 +# created: 2024-09-16T21:04:09.091105552Z diff --git a/.kokoro/release.sh b/.kokoro/release.sh index 9b17b081a..aeefd50f2 100755 --- a/.kokoro/release.sh +++ b/.kokoro/release.sh @@ -23,7 +23,7 @@ python3 -m releasetool publish-reporter-script > /tmp/publisher-script; source / export PYTHONUNBUFFERED=1 # Move into the package, build the distribution and upload. -TWINE_PASSWORD=$(cat "${KOKORO_KEYSTORE_DIR}/73713_google-cloud-pypi-token-keystore-1") +TWINE_PASSWORD=$(cat "${KOKORO_KEYSTORE_DIR}/73713_google-cloud-pypi-token-keystore-2") cd github/python-pubsub python3 setup.py sdist bdist_wheel twine upload --username __token__ --password "${TWINE_PASSWORD}" dist/* diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg index 5b1bbe360..8638067fa 100644 --- a/.kokoro/release/common.cfg +++ b/.kokoro/release/common.cfg @@ -28,7 +28,7 @@ before_action { fetch_keystore { keystore_resource { keystore_config_id: 73713 - keyname: "google-cloud-pypi-token-keystore-1" + keyname: "google-cloud-pypi-token-keystore-2" } } } From bc13ff05c3d1104c17169c360bdc09340430da37 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Fri, 27 Sep 2024 01:00:33 -0400 Subject: [PATCH 099/190] feat: Add OpenTelemetry publish sample (#1258) --- samples/snippets/publisher.py | 88 +++++++++++++++++++++++++++++++ samples/snippets/requirements.txt | 3 ++ 2 files changed, 91 insertions(+) diff --git a/samples/snippets/publisher.py b/samples/snippets/publisher.py index 73afc8c97..d2be927b8 100644 --- a/samples/snippets/publisher.py +++ b/samples/snippets/publisher.py @@ -170,6 +170,83 @@ def delete_topic(project_id: str, topic_id: str) -> None: # [END pubsub_delete_topic] +def pubsub_publish_otel_tracing( + topic_project_id: str, trace_project_id: str, topic_id: str +) -> None: + """ + Publish to `topic_id` in `topic_project_id` with OpenTelemetry enabled. + Export the OpenTelemetry traces to Google Cloud Trace in project + `trace_project_id` + + Args: + topic_project_id: project ID of the topic to publish to. + trace_project_id: project ID to export Cloud Trace to. + topic_id: topic ID to publish to. + + Returns: + None + """ + # [START pubsub_publish_otel_tracing] + + from opentelemetry import trace + from opentelemetry.sdk.trace import TracerProvider + from opentelemetry.sdk.trace.export import ( + BatchSpanProcessor, + ) + from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter + from opentelemetry.sdk.trace.sampling import TraceIdRatioBased, ParentBased + + from google.cloud.pubsub_v1 import PublisherClient + from google.cloud.pubsub_v1.types import PublisherOptions + + # TODO(developer) + # topic_project_id = "your-topic-project-id" + # trace_project_id = "your-trace-project-id" + # topic_id = "your-topic-id" + + # In this sample, we use a Google Cloud Trace to export the OpenTelemetry + # traces: https://cloud.google.com/trace/docs/setup/python-ot + # Choose and configure the exporter for your set up accordingly. + + sampler = ParentBased(root=TraceIdRatioBased(1)) + trace.set_tracer_provider(TracerProvider(sampler=sampler)) + + # Export to Google Trace. + cloud_trace_exporter = CloudTraceSpanExporter( + project_id=trace_project_id, + ) + trace.get_tracer_provider().add_span_processor( + BatchSpanProcessor(cloud_trace_exporter) + ) + + # Set the `enable_open_telemetry_tracing` option to True when creating + # the publisher client. This in itself is necessary and sufficient for + # the library to export OpenTelemetry traces. However, where the traces + # must be exported to needs to be configured based on your OpenTelemetry + # set up. Refer: https://opentelemetry.io/docs/languages/python/exporters/ + publisher = PublisherClient( + publisher_options=PublisherOptions( + enable_open_telemetry_tracing=True, + ), + ) + + # The `topic_path` method creates a fully qualified identifier + # in the form `projects/{project_id}/topics/{topic_id}` + topic_path = publisher.topic_path(topic_project_id, topic_id) + # Publish messages. + for n in range(1, 10): + data_str = f"Message number {n}" + # Data must be a bytestring + data = data_str.encode("utf-8") + # When you publish a message, the client returns a future. + future = publisher.publish(topic_path, data) + print(future.result()) + + print(f"Published messages to {topic_path}.") + + # [END pubsub_publish_otel_tracing] + + def publish_messages(project_id: str, topic_id: str) -> None: """Publishes multiple messages to a Pub/Sub topic.""" # [START pubsub_quickstart_publisher] @@ -522,6 +599,13 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: create_parser = subparsers.add_parser("create", help=create_topic.__doc__) create_parser.add_argument("topic_id") + pubsub_publish_otel_tracing_parser = subparsers.add_parser( + "pubsub-publish-otel-tracing", help=pubsub_publish_otel_tracing.__doc__ + ) + pubsub_publish_otel_tracing_parser.add_argument("topic_project_id") + pubsub_publish_otel_tracing_parser.add_argument("trace_project_id") + pubsub_publish_otel_tracing_parser.add_argument("topic_id") + create_topic_with_kinesis_ingestion_parser = subparsers.add_parser( "create_kinesis_ingestion", help=create_topic_with_kinesis_ingestion.__doc__ ) @@ -638,3 +722,7 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: resume_publish_with_ordering_keys(args.project_id, args.topic_id) elif args.command == "detach-subscription": detach_subscription(args.project_id, args.subscription_id) + elif args.command == "pubsub-publish-otel-tracing": + pubsub_publish_otel_tracing( + args.topic_project_id, args.trace_project_id, args.topic_id + ) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index b2dfe2d92..f410f8f62 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -3,3 +3,6 @@ avro==1.12.0 protobuf===4.24.4; python_version == '3.7' protobuf==5.28.0; python_version >= '3.8' avro==1.12.0 +opentelemetry-api==1.22.0 +opentelemetry-sdk==1.22.0 +opentelemetry-exporter-gcp-trace==1.7.0 \ No newline at end of file From 45a251d7da147c157711b6c4f3e4a8dac8fa7c0d Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Sat, 28 Sep 2024 12:43:34 -0400 Subject: [PATCH 100/190] doc: Subsribe sample (#1260) --- samples/snippets/subscriber.py | 88 ++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/samples/snippets/subscriber.py b/samples/snippets/subscriber.py index 79cd0ebf1..7931b31cb 100644 --- a/samples/snippets/subscriber.py +++ b/samples/snippets/subscriber.py @@ -68,6 +68,94 @@ def list_subscriptions_in_project(project_id: str) -> None: # [END pubsub_list_subscriptions] +def pubsub_subscribe_otel_tracing( + subscription_project_id: str, + cloud_trace_project_id: str, + subscription_id: str, + timeout: Optional[float] = None, +) -> None: + """ + Subscribe to `subscription_id` in `subscription_project_id` with OpenTelemetry enabled. + Export the OpenTelemetry traces to Google Cloud Trace in project + `trace_project_id` + Args: + subscription_project_id: project ID of the subscription. + cloud_trace_project_id: project ID to export Cloud Trace to. + subscription_id: subscription ID to subscribe from. + timeout: time until which to subscribe to. + Returns: + None + """ + # [START pubsub_subscribe_otel_tracing] + from opentelemetry import trace + from opentelemetry.sdk.trace import TracerProvider + from opentelemetry.sdk.trace.export import ( + BatchSpanProcessor, + ) + from opentelemetry.exporter.cloud_trace import CloudTraceSpanExporter + from opentelemetry.sdk.trace.sampling import TraceIdRatioBased, ParentBased + + from google.cloud import pubsub_v1 + from google.cloud.pubsub_v1 import SubscriberClient + from google.cloud.pubsub_v1.types import SubscriberOptions + + # TODO(developer) + # subscription_project_id = "your-subscription-project-id" + # subscription_id = "your-subscription-id" + # cloud_trace_project_id = "your-cloud-trace-project-id" + # timeout = 300.0 + + # In this sample, we use a Google Cloud Trace to export the OpenTelemetry + # traces: https://cloud.google.com/trace/docs/setup/python-ot + # Choose and configure the exporter for your set up accordingly. + + sampler = ParentBased(root=TraceIdRatioBased(1)) + trace.set_tracer_provider(TracerProvider(sampler=sampler)) + + # Export to Google Trace + cloud_trace_exporter = CloudTraceSpanExporter( + project_id=cloud_trace_project_id, + ) + trace.get_tracer_provider().add_span_processor( + BatchSpanProcessor(cloud_trace_exporter) + ) + # Set the `enable_open_telemetry_tracing` option to True when creating + # the subscriber client. This in itself is necessary and sufficient for + # the library to export OpenTelemetry traces. However, where the traces + # must be exported to needs to be configured based on your OpenTelemetry + # set up. Refer: https://opentelemetry.io/docs/languages/python/exporters/ + subscriber = SubscriberClient( + subscriber_options=SubscriberOptions(enable_open_telemetry_tracing=True) + ) + + # The `subscription_path` method creates a fully qualified identifier + # in the form `projects/{project_id}/subscriptions/{subscription_id}` + subscription_path = subscriber.subscription_path( + subscription_project_id, subscription_id + ) + + # Define callback to be called when a message is received. + def callback(message: pubsub_v1.subscriber.message.Message) -> None: + # Ack message after processing it. + print(message.data) + message.ack() + + # Wrap subscriber in a 'with' block to automatically call close() when done. + with subscriber: + try: + # Optimistically subscribe to messages on the subscription. + streaming_pull_future = subscriber.subscribe( + subscription_path, callback=callback + ) + streaming_pull_future.result(timeout=timeout) + except TimeoutError: + print("Successfully subscribed until the timeout passed.") + streaming_pull_future.cancel() # Trigger the shutdown. + streaming_pull_future.result() # Block until the shutdown is complete. + + # [END pubsub_subscribe_otel_tracing] + + def create_subscription(project_id: str, topic_id: str, subscription_id: str) -> None: """Create a new pull subscription on the given topic.""" # [START pubsub_create_pull_subscription] From e6d08e7680c5236198bffd05e577440b496e0c13 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Sun, 29 Sep 2024 01:24:37 -0400 Subject: [PATCH 101/190] chore(main): release 2.25.0 (#1262) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 28ce5d0cc..787a68e1d 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.24.0" + ".": "2.25.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 447013720..290245b2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.25.0](https://github.com/googleapis/python-pubsub/compare/v2.24.0...v2.25.0) (2024-09-28) + + +### Features + +* Add OpenTelemetry publish sample ([#1258](https://github.com/googleapis/python-pubsub/issues/1258)) ([bc13ff0](https://github.com/googleapis/python-pubsub/commit/bc13ff05c3d1104c17169c360bdc09340430da37)) + ## [2.24.0](https://github.com/googleapis/python-pubsub/compare/v2.23.1...v2.24.0) (2024-09-24) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 07de09d56..e5fa8f60b 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.24.0" # {x-release-please-version} +__version__ = "2.25.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 07de09d56..e5fa8f60b 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.24.0" # {x-release-please-version} +__version__ = "2.25.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index fd163b590..e6accad79 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.24.0" + "version": "2.25.0" }, "snippets": [ { From 5cce8b103ab7085613b7ee0efb5c8342d41ebae1 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Sun, 29 Sep 2024 19:57:48 -0400 Subject: [PATCH 102/190] fix: Update the requirements.txt for samples directory (#1263) --- samples/snippets/requirements.txt | 10 ++++++---- setup.py | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index f410f8f62..4e86dfa5b 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,8 +1,10 @@ -google-cloud-pubsub==2.23.0 +google-cloud-pubsub==2.25.0 avro==1.12.0 protobuf===4.24.4; python_version == '3.7' protobuf==5.28.0; python_version >= '3.8' avro==1.12.0 -opentelemetry-api==1.22.0 -opentelemetry-sdk==1.22.0 -opentelemetry-exporter-gcp-trace==1.7.0 \ No newline at end of file +opentelemetry-api==1.22.0; python_version == '3.7' +opentelemetry-sdk==1.22.0; python_version == '3.7' +opentelemetry-api==1.27.0; python_version >= '3.8' +opentelemetry-sdk==1.27.0; python_version >= '3.8' +opentelemetry-exporter-gcp-trace==1.7.0 diff --git a/setup.py b/setup.py index cc852f7d8..8339e1e18 100644 --- a/setup.py +++ b/setup.py @@ -45,8 +45,10 @@ "protobuf>=3.20.2,<6.0.0dev,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5", "grpc-google-iam-v1 >= 0.12.4, < 1.0.0dev", "grpcio-status >= 1.33.2", - "opentelemetry-api", - "opentelemetry-sdk", + "opentelemetry-api <= 1.22.0; python_version<='3.7'", + "opentelemetry-api >= 1.27.0; python_version>='3.8'", + "opentelemetry-sdk <= 1.22.0; python_version<='3.7'", + "opentelemetry-sdk >= 1.27.0; python_version>='3.8'", ] extras = {"libcst": "libcst >= 0.3.10"} url = "https://github.com/googleapis/python-pubsub" From a39efb329b8bf0e44e28925117c131cff21aa33d Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Sun, 29 Sep 2024 17:44:43 -0700 Subject: [PATCH 103/190] chore(main): release 2.25.1 (#1264) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 787a68e1d..ae952ab62 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.25.0" + ".": "2.25.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 290245b2b..213d21ed0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.25.1](https://github.com/googleapis/python-pubsub/compare/v2.25.0...v2.25.1) (2024-09-29) + + +### Bug Fixes + +* Update the requirements.txt for samples directory ([#1263](https://github.com/googleapis/python-pubsub/issues/1263)) ([5cce8b1](https://github.com/googleapis/python-pubsub/commit/5cce8b103ab7085613b7ee0efb5c8342d41ebae1)) + ## [2.25.0](https://github.com/googleapis/python-pubsub/compare/v2.24.0...v2.25.0) (2024-09-28) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index e5fa8f60b..bf6fc188d 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.25.0" # {x-release-please-version} +__version__ = "2.25.1" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index e5fa8f60b..bf6fc188d 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.25.0" # {x-release-please-version} +__version__ = "2.25.1" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index e6accad79..9fecd3eca 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.25.0" + "version": "2.25.1" }, "snippets": [ { From 0ff7f2a64b5aa1b0e014e0933e4edaef0fb3f222 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Mon, 30 Sep 2024 13:20:37 -0400 Subject: [PATCH 104/190] docs: Add command line args for OpenTelemetry Subscribe sample (#1265) Co-authored-by: Owl Bot --- samples/snippets/subscriber.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/samples/snippets/subscriber.py b/samples/snippets/subscriber.py index 7931b31cb..180b091db 100644 --- a/samples/snippets/subscriber.py +++ b/samples/snippets/subscriber.py @@ -1235,6 +1235,14 @@ def callback(message: pubsub_v1.subscriber.message.Message) -> None: "list-in-project", help=list_subscriptions_in_project.__doc__ ) + otel_subscribe_parse = subparsers.add_parser( + "otel-subscribe", help=pubsub_subscribe_otel_tracing.__doc__ + ) + otel_subscribe_parse.add_argument("subscription_project_id") + otel_subscribe_parse.add_argument("cloud_trace_project_id") + otel_subscribe_parse.add_argument("subscription_id") + otel_subscribe_parse.add_argument("timeout", default=None, type=float, nargs="?") + create_parser = subparsers.add_parser("create", help=create_subscription.__doc__) create_parser.add_argument("topic_id") create_parser.add_argument("subscription_id") @@ -1516,3 +1524,10 @@ def callback(message: pubsub_v1.subscriber.message.Message) -> None: receive_messages_with_concurrency_control( args.project_id, args.subscription_id, args.timeout ) + elif args.command == "otel-subscribe": + pubsub_subscribe_otel_tracing( + args.subscription_project_id, + args.cloud_trace_project_id, + args.subscription_id, + args.timeout, + ) From 5aa27805720c8d1da396995de8bb6bfce0c51106 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Mon, 30 Sep 2024 11:08:38 -0700 Subject: [PATCH 105/190] chore(main): release 2.25.2 (#1266) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index ae952ab62..f89e45276 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.25.1" + ".": "2.25.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 213d21ed0..354728b6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.25.2](https://github.com/googleapis/python-pubsub/compare/v2.25.1...v2.25.2) (2024-09-30) + + +### Documentation + +* Add command line args for OpenTelemetry Subscribe sample ([#1265](https://github.com/googleapis/python-pubsub/issues/1265)) ([0ff7f2a](https://github.com/googleapis/python-pubsub/commit/0ff7f2a64b5aa1b0e014e0933e4edaef0fb3f222)) + ## [2.25.1](https://github.com/googleapis/python-pubsub/compare/v2.25.0...v2.25.1) (2024-09-29) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index bf6fc188d..8c95d3fd6 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.25.1" # {x-release-please-version} +__version__ = "2.25.2" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index bf6fc188d..8c95d3fd6 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.25.1" # {x-release-please-version} +__version__ = "2.25.2" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 9fecd3eca..f9f4a71c0 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.25.1" + "version": "2.25.2" }, "snippets": [ { From 16a9a4a7287d6730b696a6bb3d78dcf2870beeaf Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Thu, 3 Oct 2024 22:01:05 +0200 Subject: [PATCH 106/190] chore(deps): update all dependencies (#1247) Co-authored-by: Owl Bot From a7a4caaa5a73e9b15369471dc892688e24bf52e0 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 22:34:30 -0700 Subject: [PATCH 107/190] feat: add ingestion Cloud Storage fields and Platform Logging fields to Topic (#1248) Co-authored-by: Owl Bot Co-authored-by: Mike Prieto --- google/pubsub/__init__.py | 2 + google/pubsub_v1/__init__.py | 2 + google/pubsub_v1/types/__init__.py | 2 + google/pubsub_v1/types/pubsub.py | 264 +++++++++++++++++++++++++++- scripts/fixup_pubsub_v1_keywords.py | 2 +- 5 files changed, 270 insertions(+), 2 deletions(-) diff --git a/google/pubsub/__init__.py b/google/pubsub/__init__.py index d6d0a00ff..84f0b8294 100644 --- a/google/pubsub/__init__.py +++ b/google/pubsub/__init__.py @@ -55,6 +55,7 @@ from google.pubsub_v1.types.pubsub import MessageStoragePolicy from google.pubsub_v1.types.pubsub import ModifyAckDeadlineRequest from google.pubsub_v1.types.pubsub import ModifyPushConfigRequest +from google.pubsub_v1.types.pubsub import PlatformLogsSettings from google.pubsub_v1.types.pubsub import PublishRequest from google.pubsub_v1.types.pubsub import PublishResponse from google.pubsub_v1.types.pubsub import PubsubMessage @@ -127,6 +128,7 @@ "MessageStoragePolicy", "ModifyAckDeadlineRequest", "ModifyPushConfigRequest", + "PlatformLogsSettings", "PublishRequest", "PublishResponse", "PubsubMessage", diff --git a/google/pubsub_v1/__init__.py b/google/pubsub_v1/__init__.py index 61b89e6b1..fd7ecb6d4 100644 --- a/google/pubsub_v1/__init__.py +++ b/google/pubsub_v1/__init__.py @@ -53,6 +53,7 @@ from .types.pubsub import MessageStoragePolicy from .types.pubsub import ModifyAckDeadlineRequest from .types.pubsub import ModifyPushConfigRequest +from .types.pubsub import PlatformLogsSettings from .types.pubsub import PublishRequest from .types.pubsub import PublishResponse from .types.pubsub import PubsubMessage @@ -132,6 +133,7 @@ "MessageStoragePolicy", "ModifyAckDeadlineRequest", "ModifyPushConfigRequest", + "PlatformLogsSettings", "PublishRequest", "PublishResponse", "PublisherClient", diff --git a/google/pubsub_v1/types/__init__.py b/google/pubsub_v1/types/__init__.py index 62568bf66..6feefc154 100644 --- a/google/pubsub_v1/types/__init__.py +++ b/google/pubsub_v1/types/__init__.py @@ -44,6 +44,7 @@ MessageStoragePolicy, ModifyAckDeadlineRequest, ModifyPushConfigRequest, + PlatformLogsSettings, PublishRequest, PublishResponse, PubsubMessage, @@ -122,6 +123,7 @@ "MessageStoragePolicy", "ModifyAckDeadlineRequest", "ModifyPushConfigRequest", + "PlatformLogsSettings", "PublishRequest", "PublishResponse", "PubsubMessage", diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index 5f8266014..d40d7c24e 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -31,6 +31,7 @@ "MessageStoragePolicy", "SchemaSettings", "IngestionDataSourceSettings", + "PlatformLogsSettings", "Topic", "PubsubMessage", "GetTopicRequest", @@ -160,6 +161,11 @@ class SchemaSettings(proto.Message): class IngestionDataSourceSettings(proto.Message): r"""Settings for an ingestion data source on a topic. + This message has `oneof`_ fields (mutually exclusive fields). + For each oneof, at most one member field can be set at the same time. + Setting any member of the oneof automatically clears all other + members. + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields Attributes: @@ -167,6 +173,13 @@ class IngestionDataSourceSettings(proto.Message): Optional. Amazon Kinesis Data Streams. This field is a member of `oneof`_ ``source``. + cloud_storage (google.pubsub_v1.types.IngestionDataSourceSettings.CloudStorage): + Optional. Cloud Storage. + + This field is a member of `oneof`_ ``source``. + platform_logs_settings (google.pubsub_v1.types.PlatformLogsSettings): + Optional. Platform Logs settings. If unset, + no Platform Logs will be generated. """ class AwsKinesis(proto.Message): @@ -259,12 +272,227 @@ class State(proto.Enum): number=5, ) + class CloudStorage(proto.Message): + r"""Ingestion settings for Cloud Storage. + + This message has `oneof`_ fields (mutually exclusive fields). + For each oneof, at most one member field can be set at the same time. + Setting any member of the oneof automatically clears all other + members. + + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields + + Attributes: + state (google.pubsub_v1.types.IngestionDataSourceSettings.CloudStorage.State): + Output only. An output-only field that + indicates the state of the Cloud Storage + ingestion source. + bucket (str): + Optional. Cloud Storage bucket. The bucket name must be + without any prefix like "gs://". See the [bucket naming + requirements] + (https://cloud.google.com/storage/docs/buckets#naming). + text_format (google.pubsub_v1.types.IngestionDataSourceSettings.CloudStorage.TextFormat): + Optional. Data from Cloud Storage will be + interpreted as text. + + This field is a member of `oneof`_ ``input_format``. + avro_format (google.pubsub_v1.types.IngestionDataSourceSettings.CloudStorage.AvroFormat): + Optional. Data from Cloud Storage will be + interpreted in Avro format. + + This field is a member of `oneof`_ ``input_format``. + pubsub_avro_format (google.pubsub_v1.types.IngestionDataSourceSettings.CloudStorage.PubSubAvroFormat): + Optional. It will be assumed data from Cloud Storage was + written via `Cloud Storage + subscriptions `__. + + This field is a member of `oneof`_ ``input_format``. + minimum_object_create_time (google.protobuf.timestamp_pb2.Timestamp): + Optional. Only objects with a larger or equal + creation timestamp will be ingested. + match_glob (str): + Optional. Glob pattern used to match objects that will be + ingested. If unset, all objects will be ingested. See the + `supported + patterns `__. + """ + + class State(proto.Enum): + r"""Possible states for ingestion from Cloud Storage. + + Values: + STATE_UNSPECIFIED (0): + Default value. This value is unused. + ACTIVE (1): + Ingestion is active. + CLOUD_STORAGE_PERMISSION_DENIED (2): + Permission denied encountered while calling the Cloud + Storage API. This can happen if the Pub/Sub SA has not been + granted the `appropriate + permissions `__: + + - storage.objects.list: to list the objects in a bucket. + - storage.objects.get: to read the objects in a bucket. + - storage.buckets.get: to verify the bucket exists. + PUBLISH_PERMISSION_DENIED (3): + Permission denied encountered while publishing to the topic. + This can happen if the Pub/Sub SA has not been granted the + `appropriate publish + permissions `__ + BUCKET_NOT_FOUND (4): + The provided Cloud Storage bucket doesn't + exist. + TOO_MANY_OBJECTS (5): + The Cloud Storage bucket has too many + objects, ingestion will be paused. + """ + STATE_UNSPECIFIED = 0 + ACTIVE = 1 + CLOUD_STORAGE_PERMISSION_DENIED = 2 + PUBLISH_PERMISSION_DENIED = 3 + BUCKET_NOT_FOUND = 4 + TOO_MANY_OBJECTS = 5 + + class TextFormat(proto.Message): + r"""Configuration for reading Cloud Storage data in text format. Each + line of text as specified by the delimiter will be set to the + ``data`` field of a Pub/Sub message. + + + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields + + Attributes: + delimiter (str): + Optional. When unset, '\n' is used. + + This field is a member of `oneof`_ ``_delimiter``. + """ + + delimiter: str = proto.Field( + proto.STRING, + number=1, + optional=True, + ) + + class AvroFormat(proto.Message): + r"""Configuration for reading Cloud Storage data in Avro binary format. + The bytes of each object will be set to the ``data`` field of a + Pub/Sub message. + + """ + + class PubSubAvroFormat(proto.Message): + r"""Configuration for reading Cloud Storage data written via `Cloud + Storage + subscriptions `__. + The data and attributes fields of the originally exported Pub/Sub + message will be restored when publishing. + + """ + + state: "IngestionDataSourceSettings.CloudStorage.State" = proto.Field( + proto.ENUM, + number=1, + enum="IngestionDataSourceSettings.CloudStorage.State", + ) + bucket: str = proto.Field( + proto.STRING, + number=2, + ) + text_format: "IngestionDataSourceSettings.CloudStorage.TextFormat" = ( + proto.Field( + proto.MESSAGE, + number=3, + oneof="input_format", + message="IngestionDataSourceSettings.CloudStorage.TextFormat", + ) + ) + avro_format: "IngestionDataSourceSettings.CloudStorage.AvroFormat" = ( + proto.Field( + proto.MESSAGE, + number=4, + oneof="input_format", + message="IngestionDataSourceSettings.CloudStorage.AvroFormat", + ) + ) + pubsub_avro_format: "IngestionDataSourceSettings.CloudStorage.PubSubAvroFormat" = proto.Field( + proto.MESSAGE, + number=5, + oneof="input_format", + message="IngestionDataSourceSettings.CloudStorage.PubSubAvroFormat", + ) + minimum_object_create_time: timestamp_pb2.Timestamp = proto.Field( + proto.MESSAGE, + number=6, + message=timestamp_pb2.Timestamp, + ) + match_glob: str = proto.Field( + proto.STRING, + number=9, + ) + aws_kinesis: AwsKinesis = proto.Field( proto.MESSAGE, number=1, oneof="source", message=AwsKinesis, ) + cloud_storage: CloudStorage = proto.Field( + proto.MESSAGE, + number=2, + oneof="source", + message=CloudStorage, + ) + platform_logs_settings: "PlatformLogsSettings" = proto.Field( + proto.MESSAGE, + number=4, + message="PlatformLogsSettings", + ) + + +class PlatformLogsSettings(proto.Message): + r"""Settings for Platform Logs produced by Pub/Sub. + + Attributes: + severity (google.pubsub_v1.types.PlatformLogsSettings.Severity): + Optional. The minimum severity level of + Platform Logs that will be written. + """ + + class Severity(proto.Enum): + r"""Severity levels of Platform Logs. + + Values: + SEVERITY_UNSPECIFIED (0): + Default value. Logs level is unspecified. + Logs will be disabled. + DISABLED (1): + Logs will be disabled. + DEBUG (2): + Debug logs and higher-severity logs will be + written. + INFO (3): + Info logs and higher-severity logs will be + written. + WARNING (4): + Warning logs and higher-severity logs will be + written. + ERROR (5): + Only error logs will be written. + """ + SEVERITY_UNSPECIFIED = 0 + DISABLED = 1 + DEBUG = 2 + INFO = 3 + WARNING = 4 + ERROR = 5 + + severity: Severity = proto.Field( + proto.ENUM, + number=1, + enum=Severity, + ) class Topic(proto.Message): @@ -821,7 +1049,7 @@ class Subscription(proto.Message): published. If ``retain_acked_messages`` is true, then this also configures the retention of acknowledged messages, and thus configures how far back in time a ``Seek`` can be done. - Defaults to 7 days. Cannot be more than 7 days or less than + Defaults to 7 days. Cannot be more than 31 days or less than 10 minutes. labels (MutableMapping[str, str]): Optional. See `Creating and managing @@ -906,6 +1134,10 @@ class Subscription(proto.Message): Output only. An output-only field indicating whether or not the subscription can receive messages. + analytics_hub_subscription_info (google.pubsub_v1.types.Subscription.AnalyticsHubSubscriptionInfo): + Output only. Information about the associated + Analytics Hub subscription. Only set if the + subscritpion is created by Analytics Hub. """ class State(proto.Enum): @@ -927,6 +1159,31 @@ class State(proto.Enum): ACTIVE = 1 RESOURCE_ERROR = 2 + class AnalyticsHubSubscriptionInfo(proto.Message): + r"""Information about an associated Analytics Hub subscription + (https://cloud.google.com/bigquery/docs/analytics-hub-manage-subscriptions). + + Attributes: + listing (str): + Optional. The name of the associated Analytics Hub listing + resource. Pattern: + "projects/{project}/locations/{location}/dataExchanges/{data_exchange}/listings/{listing}". + subscription (str): + Optional. The name of the associated + Analytics Hub subscription resource. Pattern: + + "projects/{project}/locations/{location}/subscriptions/{subscription}". + """ + + listing: str = proto.Field( + proto.STRING, + number=1, + ) + subscription: str = proto.Field( + proto.STRING, + number=2, + ) + name: str = proto.Field( proto.STRING, number=1, @@ -1009,6 +1266,11 @@ class State(proto.Enum): number=19, enum=State, ) + analytics_hub_subscription_info: AnalyticsHubSubscriptionInfo = proto.Field( + proto.MESSAGE, + number=23, + message=AnalyticsHubSubscriptionInfo, + ) class RetryPolicy(proto.Message): diff --git a/scripts/fixup_pubsub_v1_keywords.py b/scripts/fixup_pubsub_v1_keywords.py index 35262ea8d..543f7e051 100644 --- a/scripts/fixup_pubsub_v1_keywords.py +++ b/scripts/fixup_pubsub_v1_keywords.py @@ -43,7 +43,7 @@ class pubsubCallTransformer(cst.CSTTransformer): 'commit_schema': ('name', 'schema', ), 'create_schema': ('parent', 'schema', 'schema_id', ), 'create_snapshot': ('name', 'subscription', 'labels', ), - 'create_subscription': ('name', 'topic', 'push_config', 'bigquery_config', 'cloud_storage_config', 'ack_deadline_seconds', 'retain_acked_messages', 'message_retention_duration', 'labels', 'enable_message_ordering', 'expiration_policy', 'filter', 'dead_letter_policy', 'retry_policy', 'detached', 'enable_exactly_once_delivery', 'topic_message_retention_duration', 'state', ), + 'create_subscription': ('name', 'topic', 'push_config', 'bigquery_config', 'cloud_storage_config', 'ack_deadline_seconds', 'retain_acked_messages', 'message_retention_duration', 'labels', 'enable_message_ordering', 'expiration_policy', 'filter', 'dead_letter_policy', 'retry_policy', 'detached', 'enable_exactly_once_delivery', 'topic_message_retention_duration', 'state', 'analytics_hub_subscription_info', ), 'create_topic': ('name', 'labels', 'message_storage_policy', 'kms_key_name', 'schema_settings', 'satisfies_pzs', 'message_retention_duration', 'state', 'ingestion_data_source_settings', ), 'delete_schema': ('name', ), 'delete_schema_revision': ('name', 'revision_id', ), From c240fde4db5ec68ceab8b4ea85dff6a825230453 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:11:20 -0400 Subject: [PATCH 108/190] chore(main): release 2.26.0 (#1272) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f89e45276..d53227a83 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.25.2" + ".": "2.26.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 354728b6d..e1a67b711 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.26.0](https://github.com/googleapis/python-pubsub/compare/v2.25.2...v2.26.0) (2024-10-09) + + +### Features + +* Add ingestion Cloud Storage fields and Platform Logging fields to Topic ([#1248](https://github.com/googleapis/python-pubsub/issues/1248)) ([a7a4caa](https://github.com/googleapis/python-pubsub/commit/a7a4caaa5a73e9b15369471dc892688e24bf52e0)) + ## [2.25.2](https://github.com/googleapis/python-pubsub/compare/v2.25.1...v2.25.2) (2024-09-30) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 8c95d3fd6..d56eed5c5 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.25.2" # {x-release-please-version} +__version__ = "2.26.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 8c95d3fd6..d56eed5c5 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.25.2" # {x-release-please-version} +__version__ = "2.26.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index f9f4a71c0..a20353b05 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.25.2" + "version": "2.26.0" }, "snippets": [ { From b59cc8d4fae593eb7592455a1696d7ab996a53dd Mon Sep 17 00:00:00 2001 From: Mike Prieto Date: Thu, 10 Oct 2024 17:42:32 -0400 Subject: [PATCH 109/190] docs: Add ingestion from GCS sample (#1273) --- samples/snippets/publisher.py | 105 +++++++++++++++++++++++++++++ samples/snippets/publisher_test.py | 34 ++++++++++ samples/snippets/requirements.txt | 2 +- 3 files changed, 140 insertions(+), 1 deletion(-) diff --git a/samples/snippets/publisher.py b/samples/snippets/publisher.py index d2be927b8..7cb7ca223 100644 --- a/samples/snippets/publisher.py +++ b/samples/snippets/publisher.py @@ -103,6 +103,88 @@ def create_topic_with_kinesis_ingestion( # [END pubsub_create_topic_with_kinesis_ingestion] +def create_topic_with_cloud_storage_ingestion( + project_id: str, + topic_id: str, + bucket: str, + input_format: str, + text_delimiter: str, + match_glob: str, + minimum_object_create_time: str, +) -> None: + """Create a new Pub/Sub topic with Cloud Storage Ingestion Settings.""" + # [START pubsub_create_topic_with_cloud_storage_ingestion] + from google.cloud import pubsub_v1 + from google.protobuf import timestamp_pb2 + from google.pubsub_v1.types import Topic + from google.pubsub_v1.types import IngestionDataSourceSettings + + # TODO(developer) + # project_id = "your-project-id" + # topic_id = "your-topic-id" + # bucket = "your-bucket" + # input_format = "text" (can be one of "text", "avro", "pubsub_avro") + # text_delimiter = "\n" + # match_glob = "**.txt" + # minimum_object_create_time = "YYYY-MM-DDThh:mm:ssZ" + + publisher = pubsub_v1.PublisherClient() + topic_path = publisher.topic_path(project_id, topic_id) + + cloud_storage_settings = IngestionDataSourceSettings.CloudStorage( + bucket=bucket, + ) + if input_format == "text": + cloud_storage_settings.text_format = ( + IngestionDataSourceSettings.CloudStorage.TextFormat( + delimiter=text_delimiter + ) + ) + elif input_format == "avro": + cloud_storage_settings.avro_format = ( + IngestionDataSourceSettings.CloudStorage.AvroFormat() + ) + elif input_format == "pubsub_avro": + cloud_storage_settings.pubsub_avro_format = ( + IngestionDataSourceSettings.CloudStorage.PubSubAvroFormat() + ) + else: + print( + "Invalid input_format: " + + input_format + + "; must be in ('text', 'avro', 'pubsub_avro')" + ) + return + + if match_glob: + cloud_storage_settings.match_glob = match_glob + + if minimum_object_create_time: + try: + minimum_object_create_time_timestamp = timestamp_pb2.Timestamp() + minimum_object_create_time_timestamp.FromJsonString( + minimum_object_create_time + ) + cloud_storage_settings.minimum_object_create_time = ( + minimum_object_create_time_timestamp + ) + except ValueError: + print("Invalid minimum_object_create_time: " + minimum_object_create_time) + return + + request = Topic( + name=topic_path, + ingestion_data_source_settings=IngestionDataSourceSettings( + cloud_storage=cloud_storage_settings, + ), + ) + + topic = publisher.create_topic(request=request) + + print(f"Created topic: {topic.name} with Cloud Storage Ingestion Settings") + # [END pubsub_create_topic_with_cloud_storage_ingestion] + + def update_topic_type( project_id: str, topic_id: str, @@ -615,6 +697,19 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: create_topic_with_kinesis_ingestion_parser.add_argument("aws_role_arn") create_topic_with_kinesis_ingestion_parser.add_argument("gcp_service_account") + create_topic_with_cloud_storage_ingestion_parser = subparsers.add_parser( + "create_cloud_storage_ingestion", + help=create_topic_with_cloud_storage_ingestion.__doc__, + ) + create_topic_with_cloud_storage_ingestion_parser.add_argument("topic_id") + create_topic_with_cloud_storage_ingestion_parser.add_argument("bucket") + create_topic_with_cloud_storage_ingestion_parser.add_argument("input_format") + create_topic_with_cloud_storage_ingestion_parser.add_argument("text_delimiter") + create_topic_with_cloud_storage_ingestion_parser.add_argument("match_glob") + create_topic_with_cloud_storage_ingestion_parser.add_argument( + "minimum_object_create_time" + ) + update_topic_type_parser = subparsers.add_parser( "update_kinesis_ingestion", help=update_topic_type.__doc__ ) @@ -693,6 +788,16 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: args.aws_role_arn, args.gcp_service_account, ) + elif args.command == "create_cloud_storage_ingestion": + create_topic_with_cloud_storage_ingestion( + args.project_id, + args.topic_id, + args.bucket, + args.input_format, + args.text_delimiter, + args.match_glob, + args.minimum_object_create_time, + ) elif args.command == "update_kinesis_ingestion": update_topic_type( args.project_id, diff --git a/samples/snippets/publisher_test.py b/samples/snippets/publisher_test.py index adb015e8a..6f17305cb 100644 --- a/samples/snippets/publisher_test.py +++ b/samples/snippets/publisher_test.py @@ -162,6 +162,40 @@ def test_create_topic_with_kinesis_ingestion( publisher_client.delete_topic(request={"topic": topic_path}) +def test_create_topic_with_cloud_storage_ingestion( + publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] +) -> None: + # The scope of `topic_path` is limited to this function. + topic_path = publisher_client.topic_path(PROJECT_ID, TOPIC_ID) + + bucket = "pubsub-cloud-storage-bucket" + input_format = "text" + text_delimiter = "," + match_glob = "**.txt" + minimum_object_create_time = "1970-01-01T00:00:01Z" + + try: + publisher_client.delete_topic(request={"topic": topic_path}) + except NotFound: + pass + + publisher.create_topic_with_cloud_storage_ingestion( + PROJECT_ID, + TOPIC_ID, + bucket, + input_format, + text_delimiter, + match_glob, + minimum_object_create_time, + ) + + out, _ = capsys.readouterr() + assert f"Created topic: {topic_path} with Cloud Storage Ingestion Settings" in out + + # Clean up resource created for the test. + publisher_client.delete_topic(request={"topic": topic_path}) + + def test_update_topic_type( publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] ) -> None: diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 4e86dfa5b..3a16ebc94 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,4 +1,4 @@ -google-cloud-pubsub==2.25.0 +google-cloud-pubsub==2.26.0 avro==1.12.0 protobuf===4.24.4; python_version == '3.7' protobuf==5.28.0; python_version >= '3.8' From 8febfc7c47fe7d9990944b0d7962803f00ec012a Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 18:51:27 -0400 Subject: [PATCH 110/190] chore(main): release 2.26.1 (#1276) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index d53227a83..9c3477bd7 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.26.0" + ".": "2.26.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index e1a67b711..64bb863eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.26.1](https://github.com/googleapis/python-pubsub/compare/v2.26.0...v2.26.1) (2024-10-10) + + +### Documentation + +* Add ingestion from GCS sample ([#1273](https://github.com/googleapis/python-pubsub/issues/1273)) ([b59cc8d](https://github.com/googleapis/python-pubsub/commit/b59cc8d4fae593eb7592455a1696d7ab996a53dd)) + ## [2.26.0](https://github.com/googleapis/python-pubsub/compare/v2.25.2...v2.26.0) (2024-10-09) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index d56eed5c5..040d4e7f1 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.26.0" # {x-release-please-version} +__version__ = "2.26.1" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index d56eed5c5..040d4e7f1 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.26.0" # {x-release-please-version} +__version__ = "2.26.1" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index a20353b05..ead6d83f3 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.26.0" + "version": "2.26.1" }, "snippets": [ { From 5cb2e2aa8031ff1acd044cb29f012b40f93895af Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:57:02 -0400 Subject: [PATCH 111/190] build: use multiScm for Kokoro release builds (#1284) Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 +- .github/release-trigger.yml | 1 + .kokoro/docker/docs/requirements.txt | 42 +- .kokoro/docs/common.cfg | 2 +- .kokoro/release.sh | 2 +- .kokoro/release/common.cfg | 8 +- .kokoro/requirements.txt | 610 +++++++++---------- .kokoro/samples/python3.13/common.cfg | 40 ++ .kokoro/samples/python3.13/continuous.cfg | 6 + .kokoro/samples/python3.13/periodic-head.cfg | 11 + .kokoro/samples/python3.13/periodic.cfg | 6 + .kokoro/samples/python3.13/presubmit.cfg | 6 + .kokoro/test-samples-impl.sh | 3 +- noxfile.py | 6 +- samples/snippets/noxfile.py | 2 +- 15 files changed, 393 insertions(+), 356 deletions(-) create mode 100644 .kokoro/samples/python3.13/common.cfg create mode 100644 .kokoro/samples/python3.13/continuous.cfg create mode 100644 .kokoro/samples/python3.13/periodic-head.cfg create mode 100644 .kokoro/samples/python3.13/periodic.cfg create mode 100644 .kokoro/samples/python3.13/presubmit.cfg diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 597e0c326..7672b49b6 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:e8dcfd7cbfd8beac3a3ff8d3f3185287ea0625d859168cc80faccfc9a7a00455 -# created: 2024-09-16T21:04:09.091105552Z + digest: sha256:5cddfe2fb5019bbf78335bc55f15bc13e18354a56b3ff46e1834f8e540807f05 +# created: 2024-10-31T01:41:07.349286254Z diff --git a/.github/release-trigger.yml b/.github/release-trigger.yml index d4ca94189..4bb79e58e 100644 --- a/.github/release-trigger.yml +++ b/.github/release-trigger.yml @@ -1 +1,2 @@ enabled: true +multiScmName: diff --git a/.kokoro/docker/docs/requirements.txt b/.kokoro/docker/docs/requirements.txt index 7129c7715..66eacc82f 100644 --- a/.kokoro/docker/docs/requirements.txt +++ b/.kokoro/docker/docs/requirements.txt @@ -4,39 +4,39 @@ # # pip-compile --allow-unsafe --generate-hashes requirements.in # -argcomplete==3.4.0 \ - --hash=sha256:69a79e083a716173e5532e0fa3bef45f793f4e61096cf52b5a42c0211c8b8aa5 \ - --hash=sha256:c2abcdfe1be8ace47ba777d4fce319eb13bf8ad9dace8d085dcad6eded88057f +argcomplete==3.5.1 \ + --hash=sha256:1a1d148bdaa3e3b93454900163403df41448a248af01b6e849edc5ac08e6c363 \ + --hash=sha256:eb1ee355aa2557bd3d0145de7b06b2a45b0ce461e1e7813f5d066039ab4177b4 # via nox colorlog==6.8.2 \ --hash=sha256:3e3e079a41feb5a1b64f978b5ea4f46040a94f11f0e8bbb8261e3dbbeca64d44 \ --hash=sha256:4dcbb62368e2800cb3c5abd348da7e53f6c362dda502ec27c560b2e58a66bd33 # via nox -distlib==0.3.8 \ - --hash=sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784 \ - --hash=sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64 +distlib==0.3.9 \ + --hash=sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87 \ + --hash=sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403 # via virtualenv -filelock==3.15.4 \ - --hash=sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb \ - --hash=sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7 +filelock==3.16.1 \ + --hash=sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0 \ + --hash=sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435 # via virtualenv -nox==2024.4.15 \ - --hash=sha256:6492236efa15a460ecb98e7b67562a28b70da006ab0be164e8821177577c0565 \ - --hash=sha256:ecf6700199cdfa9e5ea0a41ff5e6ef4641d09508eda6edb89d9987864115817f +nox==2024.10.9 \ + --hash=sha256:1d36f309a0a2a853e9bccb76bbef6bb118ba92fa92674d15604ca99adeb29eab \ + --hash=sha256:7aa9dc8d1c27e9f45ab046ffd1c3b2c4f7c91755304769df231308849ebded95 # via -r requirements.in packaging==24.1 \ --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 # via nox -platformdirs==4.2.2 \ - --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \ - --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3 +platformdirs==4.3.6 \ + --hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \ + --hash=sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb # via virtualenv -tomli==2.0.1 \ - --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ - --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f +tomli==2.0.2 \ + --hash=sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38 \ + --hash=sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed # via nox -virtualenv==20.26.3 \ - --hash=sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a \ - --hash=sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589 +virtualenv==20.26.6 \ + --hash=sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48 \ + --hash=sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2 # via nox diff --git a/.kokoro/docs/common.cfg b/.kokoro/docs/common.cfg index 63ce88a82..a9392e09a 100644 --- a/.kokoro/docs/common.cfg +++ b/.kokoro/docs/common.cfg @@ -63,4 +63,4 @@ before_action { keyname: "docuploader_service_account" } } -} \ No newline at end of file +} diff --git a/.kokoro/release.sh b/.kokoro/release.sh index aeefd50f2..006893576 100755 --- a/.kokoro/release.sh +++ b/.kokoro/release.sh @@ -23,7 +23,7 @@ python3 -m releasetool publish-reporter-script > /tmp/publisher-script; source / export PYTHONUNBUFFERED=1 # Move into the package, build the distribution and upload. -TWINE_PASSWORD=$(cat "${KOKORO_KEYSTORE_DIR}/73713_google-cloud-pypi-token-keystore-2") +TWINE_PASSWORD=$(cat "${KOKORO_KEYSTORE_DIR}/73713_google-cloud-pypi-token-keystore-3") cd github/python-pubsub python3 setup.py sdist bdist_wheel twine upload --username __token__ --password "${TWINE_PASSWORD}" dist/* diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg index 8638067fa..a6b92c637 100644 --- a/.kokoro/release/common.cfg +++ b/.kokoro/release/common.cfg @@ -28,17 +28,11 @@ before_action { fetch_keystore { keystore_resource { keystore_config_id: 73713 - keyname: "google-cloud-pypi-token-keystore-2" + keyname: "google-cloud-pypi-token-keystore-3" } } } -# Tokens needed to report release status back to GitHub -env_vars: { - key: "SECRET_MANAGER_KEYS" - value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem" -} - # Store the packages we uploaded to PyPI. That way, we have a record of exactly # what we published, which we can use to generate SBOMs and attestations. action { diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt index 9622baf0b..006d8ef93 100644 --- a/.kokoro/requirements.txt +++ b/.kokoro/requirements.txt @@ -4,79 +4,94 @@ # # pip-compile --allow-unsafe --generate-hashes requirements.in # -argcomplete==3.4.0 \ - --hash=sha256:69a79e083a716173e5532e0fa3bef45f793f4e61096cf52b5a42c0211c8b8aa5 \ - --hash=sha256:c2abcdfe1be8ace47ba777d4fce319eb13bf8ad9dace8d085dcad6eded88057f +argcomplete==3.5.1 \ + --hash=sha256:1a1d148bdaa3e3b93454900163403df41448a248af01b6e849edc5ac08e6c363 \ + --hash=sha256:eb1ee355aa2557bd3d0145de7b06b2a45b0ce461e1e7813f5d066039ab4177b4 # via nox -attrs==23.2.0 \ - --hash=sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30 \ - --hash=sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1 +attrs==24.2.0 \ + --hash=sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346 \ + --hash=sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2 # via gcp-releasetool backports-tarfile==1.2.0 \ --hash=sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34 \ --hash=sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991 # via jaraco-context -cachetools==5.3.3 \ - --hash=sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945 \ - --hash=sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105 +cachetools==5.5.0 \ + --hash=sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292 \ + --hash=sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a # via google-auth -certifi==2024.7.4 \ - --hash=sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b \ - --hash=sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90 +certifi==2024.8.30 \ + --hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \ + --hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9 # via requests -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 +cffi==1.17.1 \ + --hash=sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8 \ + --hash=sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2 \ + --hash=sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1 \ + --hash=sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15 \ + --hash=sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36 \ + --hash=sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824 \ + --hash=sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8 \ + --hash=sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36 \ + --hash=sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17 \ + --hash=sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf \ + --hash=sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc \ + --hash=sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3 \ + --hash=sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed \ + --hash=sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702 \ + --hash=sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1 \ + --hash=sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8 \ + --hash=sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903 \ + --hash=sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6 \ + --hash=sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d \ + --hash=sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b \ + --hash=sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e \ + --hash=sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be \ + --hash=sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c \ + --hash=sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683 \ + --hash=sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9 \ + --hash=sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c \ + --hash=sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8 \ + --hash=sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1 \ + --hash=sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4 \ + --hash=sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655 \ + --hash=sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67 \ + --hash=sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595 \ + --hash=sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0 \ + --hash=sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65 \ + --hash=sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41 \ + --hash=sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6 \ + --hash=sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401 \ + --hash=sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6 \ + --hash=sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3 \ + --hash=sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16 \ + --hash=sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93 \ + --hash=sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e \ + --hash=sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4 \ + --hash=sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964 \ + --hash=sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c \ + --hash=sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576 \ + --hash=sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0 \ + --hash=sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3 \ + --hash=sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662 \ + --hash=sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3 \ + --hash=sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff \ + --hash=sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5 \ + --hash=sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd \ + --hash=sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f \ + --hash=sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5 \ + --hash=sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14 \ + --hash=sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d \ + --hash=sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9 \ + --hash=sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7 \ + --hash=sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382 \ + --hash=sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a \ + --hash=sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e \ + --hash=sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a \ + --hash=sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4 \ + --hash=sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99 \ + --hash=sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87 \ + --hash=sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b # via cryptography charset-normalizer==2.1.1 \ --hash=sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845 \ @@ -97,72 +112,67 @@ colorlog==6.8.2 \ # via # gcp-docuploader # nox -cryptography==42.0.8 \ - --hash=sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad \ - --hash=sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583 \ - --hash=sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b \ - --hash=sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c \ - --hash=sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1 \ - --hash=sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648 \ - --hash=sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949 \ - --hash=sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba \ - --hash=sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c \ - --hash=sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9 \ - --hash=sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d \ - --hash=sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c \ - --hash=sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e \ - --hash=sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2 \ - --hash=sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d \ - --hash=sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7 \ - --hash=sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70 \ - --hash=sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2 \ - --hash=sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7 \ - --hash=sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14 \ - --hash=sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe \ - --hash=sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e \ - --hash=sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71 \ - --hash=sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961 \ - --hash=sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7 \ - --hash=sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c \ - --hash=sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28 \ - --hash=sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842 \ - --hash=sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902 \ - --hash=sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801 \ - --hash=sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a \ - --hash=sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e +cryptography==43.0.1 \ + --hash=sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494 \ + --hash=sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806 \ + --hash=sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d \ + --hash=sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062 \ + --hash=sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2 \ + --hash=sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4 \ + --hash=sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1 \ + --hash=sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85 \ + --hash=sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84 \ + --hash=sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042 \ + --hash=sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d \ + --hash=sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962 \ + --hash=sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2 \ + --hash=sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa \ + --hash=sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d \ + --hash=sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365 \ + --hash=sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96 \ + --hash=sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47 \ + --hash=sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d \ + --hash=sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d \ + --hash=sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c \ + --hash=sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb \ + --hash=sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277 \ + --hash=sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172 \ + --hash=sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034 \ + --hash=sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a \ + --hash=sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289 # via # -r requirements.in # gcp-releasetool # secretstorage -distlib==0.3.8 \ - --hash=sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784 \ - --hash=sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64 +distlib==0.3.9 \ + --hash=sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87 \ + --hash=sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403 # via virtualenv docutils==0.21.2 \ --hash=sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f \ --hash=sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2 # via readme-renderer -filelock==3.15.4 \ - --hash=sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb \ - --hash=sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7 +filelock==3.16.1 \ + --hash=sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0 \ + --hash=sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435 # via virtualenv gcp-docuploader==0.6.5 \ --hash=sha256:30221d4ac3e5a2b9c69aa52fdbef68cc3f27d0e6d0d90e220fc024584b8d2318 \ --hash=sha256:b7458ef93f605b9d46a4bf3a8dc1755dad1f31d030c8679edf304e343b347eea # via -r requirements.in -gcp-releasetool==2.0.1 \ - --hash=sha256:34314a910c08e8911d9c965bd44f8f2185c4f556e737d719c33a41f6a610de96 \ - --hash=sha256:b0d5863c6a070702b10883d37c4bdfd74bf930fe417f36c0c965d3b7c779ae62 +gcp-releasetool==2.1.1 \ + --hash=sha256:25639269f4eae510094f9dbed9894977e1966933211eb155a451deebc3fc0b30 \ + --hash=sha256:845f4ded3d9bfe8cc7fdaad789e83f4ea014affa77785259a7ddac4b243e099e # via -r requirements.in -google-api-core==2.19.1 \ - --hash=sha256:f12a9b8309b5e21d92483bbd47ce2c445861ec7d269ef6784ecc0ea8c1fa6125 \ - --hash=sha256:f4695f1e3650b316a795108a76a1c416e6afb036199d1c1f1f110916df479ffd +google-api-core==2.21.0 \ + --hash=sha256:4a152fd11a9f774ea606388d423b68aa7e6d6a0ffe4c8266f74979613ec09f81 \ + --hash=sha256:6869eacb2a37720380ba5898312af79a4d30b8bca1548fb4093e0697dc4bdf5d # via # google-cloud-core # google-cloud-storage -google-auth==2.31.0 \ - --hash=sha256:042c4702efa9f7d3c48d3a69341c209381b125faa6dbf3ebe56bc7e40ae05c23 \ - --hash=sha256:87805c36970047247c8afe614d4e3af8eceafc1ebba0c679fe75ddd1d575e871 +google-auth==2.35.0 \ + --hash=sha256:25df55f327ef021de8be50bad0dfd4a916ad0de96da86cd05661c9297723ad3f \ + --hash=sha256:f4c64ed4e01e8e8b646ef34c018f8bf3338df0c8e37d8b3bba40e7f574a3278a # via # gcp-releasetool # google-api-core @@ -172,97 +182,56 @@ google-cloud-core==2.4.1 \ --hash=sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073 \ --hash=sha256:a9e6a4422b9ac5c29f79a0ede9485473338e2ce78d91f2370c01e730eab22e61 # via google-cloud-storage -google-cloud-storage==2.17.0 \ - --hash=sha256:49378abff54ef656b52dca5ef0f2eba9aa83dc2b2c72c78714b03a1a95fe9388 \ - --hash=sha256:5b393bc766b7a3bc6f5407b9e665b2450d36282614b7945e570b3480a456d1e1 +google-cloud-storage==2.18.2 \ + --hash=sha256:97a4d45c368b7d401ed48c4fdfe86e1e1cb96401c9e199e419d289e2c0370166 \ + --hash=sha256:aaf7acd70cdad9f274d29332673fcab98708d0e1f4dceb5a5356aaef06af4d99 # via gcp-docuploader -google-crc32c==1.5.0 \ - --hash=sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a \ - --hash=sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876 \ - --hash=sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c \ - --hash=sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289 \ - --hash=sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298 \ - --hash=sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02 \ - --hash=sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f \ - --hash=sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2 \ - --hash=sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a \ - --hash=sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb \ - --hash=sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210 \ - --hash=sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5 \ - --hash=sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee \ - --hash=sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c \ - --hash=sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a \ - --hash=sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314 \ - --hash=sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd \ - --hash=sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65 \ - --hash=sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37 \ - --hash=sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4 \ - --hash=sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13 \ - --hash=sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894 \ - --hash=sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31 \ - --hash=sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e \ - --hash=sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709 \ - --hash=sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740 \ - --hash=sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc \ - --hash=sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d \ - --hash=sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c \ - --hash=sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c \ - --hash=sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d \ - --hash=sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906 \ - --hash=sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61 \ - --hash=sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57 \ - --hash=sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c \ - --hash=sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a \ - --hash=sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438 \ - --hash=sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946 \ - --hash=sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7 \ - --hash=sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96 \ - --hash=sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091 \ - --hash=sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae \ - --hash=sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d \ - --hash=sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88 \ - --hash=sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2 \ - --hash=sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd \ - --hash=sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541 \ - --hash=sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728 \ - --hash=sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178 \ - --hash=sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968 \ - --hash=sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346 \ - --hash=sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8 \ - --hash=sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93 \ - --hash=sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7 \ - --hash=sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273 \ - --hash=sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462 \ - --hash=sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94 \ - --hash=sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd \ - --hash=sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e \ - --hash=sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57 \ - --hash=sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b \ - --hash=sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9 \ - --hash=sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a \ - --hash=sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100 \ - --hash=sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325 \ - --hash=sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183 \ - --hash=sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556 \ - --hash=sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4 +google-crc32c==1.6.0 \ + --hash=sha256:05e2d8c9a2f853ff116db9706b4a27350587f341eda835f46db3c0a8c8ce2f24 \ + --hash=sha256:18e311c64008f1f1379158158bb3f0c8d72635b9eb4f9545f8cf990c5668e59d \ + --hash=sha256:236c87a46cdf06384f614e9092b82c05f81bd34b80248021f729396a78e55d7e \ + --hash=sha256:35834855408429cecf495cac67ccbab802de269e948e27478b1e47dfb6465e57 \ + --hash=sha256:386122eeaaa76951a8196310432c5b0ef3b53590ef4c317ec7588ec554fec5d2 \ + --hash=sha256:40b05ab32a5067525670880eb5d169529089a26fe35dce8891127aeddc1950e8 \ + --hash=sha256:48abd62ca76a2cbe034542ed1b6aee851b6f28aaca4e6551b5599b6f3ef175cc \ + --hash=sha256:50cf2a96da226dcbff8671233ecf37bf6e95de98b2a2ebadbfdf455e6d05df42 \ + --hash=sha256:51c4f54dd8c6dfeb58d1df5e4f7f97df8abf17a36626a217f169893d1d7f3e9f \ + --hash=sha256:5bcc90b34df28a4b38653c36bb5ada35671ad105c99cfe915fb5bed7ad6924aa \ + --hash=sha256:62f6d4a29fea082ac4a3c9be5e415218255cf11684ac6ef5488eea0c9132689b \ + --hash=sha256:6eceb6ad197656a1ff49ebfbbfa870678c75be4344feb35ac1edf694309413dc \ + --hash=sha256:7aec8e88a3583515f9e0957fe4f5f6d8d4997e36d0f61624e70469771584c760 \ + --hash=sha256:91ca8145b060679ec9176e6de4f89b07363d6805bd4760631ef254905503598d \ + --hash=sha256:a184243544811e4a50d345838a883733461e67578959ac59964e43cca2c791e7 \ + --hash=sha256:a9e4b426c3702f3cd23b933436487eb34e01e00327fac20c9aebb68ccf34117d \ + --hash=sha256:bb0966e1c50d0ef5bc743312cc730b533491d60585a9a08f897274e57c3f70e0 \ + --hash=sha256:bb8b3c75bd157010459b15222c3fd30577042a7060e29d42dabce449c087f2b3 \ + --hash=sha256:bd5e7d2445d1a958c266bfa5d04c39932dc54093fa391736dbfdb0f1929c1fb3 \ + --hash=sha256:c87d98c7c4a69066fd31701c4e10d178a648c2cac3452e62c6b24dc51f9fcc00 \ + --hash=sha256:d2952396dc604544ea7476b33fe87faedc24d666fb0c2d5ac971a2b9576ab871 \ + --hash=sha256:d8797406499f28b5ef791f339594b0b5fdedf54e203b5066675c406ba69d705c \ + --hash=sha256:d9e9913f7bd69e093b81da4535ce27af842e7bf371cde42d1ae9e9bd382dc0e9 \ + --hash=sha256:e2806553238cd076f0a55bddab37a532b53580e699ed8e5606d0de1f856b5205 \ + --hash=sha256:ebab974b1687509e5c973b5c4b8b146683e101e102e17a86bd196ecaa4d099fc \ + --hash=sha256:ed767bf4ba90104c1216b68111613f0d5926fb3780660ea1198fc469af410e9d \ + --hash=sha256:f7a1fc29803712f80879b0806cb83ab24ce62fc8daf0569f2204a0cfd7f68ed4 # via # google-cloud-storage # google-resumable-media -google-resumable-media==2.7.1 \ - --hash=sha256:103ebc4ba331ab1bfdac0250f8033627a2cd7cde09e7ccff9181e31ba4315b2c \ - --hash=sha256:eae451a7b2e2cdbaaa0fd2eb00cc8a1ee5e95e16b55597359cbc3d27d7d90e33 +google-resumable-media==2.7.2 \ + --hash=sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa \ + --hash=sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0 # via google-cloud-storage -googleapis-common-protos==1.63.2 \ - --hash=sha256:27a2499c7e8aff199665b22741997e485eccc8645aa9176c7c988e6fae507945 \ - --hash=sha256:27c5abdffc4911f28101e635de1533fb4cfd2c37fbaa9174587c799fac90aa87 +googleapis-common-protos==1.65.0 \ + --hash=sha256:2972e6c496f435b92590fd54045060867f3fe9be2c82ab148fc8885035479a63 \ + --hash=sha256:334a29d07cddc3aa01dee4988f9afd9b2916ee2ff49d6b757155dc0d197852c0 # via google-api-core -idna==3.7 \ - --hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \ - --hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0 +idna==3.10 \ + --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ + --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 # via requests -importlib-metadata==8.0.0 \ - --hash=sha256:15584cf2b1bf449d98ff8a6ff1abef57bf20f3ac6454f431736cd3e660921b2f \ - --hash=sha256:188bd24e4c346d3f0a933f275c2fec67050326a856b9a359881d7c2a697e8812 +importlib-metadata==8.5.0 \ + --hash=sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b \ + --hash=sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7 # via # -r requirements.in # keyring @@ -271,13 +240,13 @@ jaraco-classes==3.4.0 \ --hash=sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd \ --hash=sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790 # via keyring -jaraco-context==5.3.0 \ - --hash=sha256:3e16388f7da43d384a1a7cd3452e72e14732ac9fe459678773a3608a812bf266 \ - --hash=sha256:c2f67165ce1f9be20f32f650f25d8edfc1646a8aeee48ae06fb35f90763576d2 +jaraco-context==6.0.1 \ + --hash=sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3 \ + --hash=sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4 # via keyring -jaraco-functools==4.0.1 \ - --hash=sha256:3b24ccb921d6b593bdceb56ce14799204f473976e2a9d4b15b04d0f2c2326664 \ - --hash=sha256:d33fa765374c0611b52f8b3a795f8900869aa88c84769d4d1746cd68fb28c3e8 +jaraco-functools==4.1.0 \ + --hash=sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d \ + --hash=sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649 # via keyring jeepney==0.8.0 \ --hash=sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806 \ @@ -289,9 +258,9 @@ jinja2==3.1.4 \ --hash=sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369 \ --hash=sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d # via gcp-releasetool -keyring==25.2.1 \ - --hash=sha256:2458681cdefc0dbc0b7eb6cf75d0b98e59f9ad9b2d4edd319d18f68bdca95e50 \ - --hash=sha256:daaffd42dbda25ddafb1ad5fec4024e5bbcfe424597ca1ca452b299861e49f1b +keyring==25.4.1 \ + --hash=sha256:5426f817cf7f6f007ba5ec722b1bcad95a75b27d780343772ad76b17cb47b0bf \ + --hash=sha256:b07ebc55f3e8ed86ac81dd31ef14e81ace9dd9c3d4b5d77a6e9a2016d0d71a1b # via # gcp-releasetool # twine @@ -299,75 +268,76 @@ markdown-it-py==3.0.0 \ --hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \ --hash=sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb # via rich -markupsafe==2.1.5 \ - --hash=sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf \ - --hash=sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff \ - --hash=sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f \ - --hash=sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3 \ - --hash=sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532 \ - --hash=sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f \ - --hash=sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617 \ - --hash=sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df \ - --hash=sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4 \ - --hash=sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906 \ - --hash=sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f \ - --hash=sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4 \ - --hash=sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8 \ - --hash=sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371 \ - --hash=sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2 \ - --hash=sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465 \ - --hash=sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52 \ - --hash=sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6 \ - --hash=sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169 \ - --hash=sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad \ - --hash=sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2 \ - --hash=sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0 \ - --hash=sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029 \ - --hash=sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f \ - --hash=sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a \ - --hash=sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced \ - --hash=sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5 \ - --hash=sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c \ - --hash=sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf \ - --hash=sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9 \ - --hash=sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb \ - --hash=sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad \ - --hash=sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3 \ - --hash=sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1 \ - --hash=sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46 \ - --hash=sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc \ - --hash=sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a \ - --hash=sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee \ - --hash=sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900 \ - --hash=sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5 \ - --hash=sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea \ - --hash=sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f \ - --hash=sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5 \ - --hash=sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e \ - --hash=sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a \ - --hash=sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f \ - --hash=sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50 \ - --hash=sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a \ - --hash=sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b \ - --hash=sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4 \ - --hash=sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff \ - --hash=sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2 \ - --hash=sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46 \ - --hash=sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b \ - --hash=sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf \ - --hash=sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5 \ - --hash=sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5 \ - --hash=sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab \ - --hash=sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd \ - --hash=sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68 +markupsafe==3.0.1 \ + --hash=sha256:0778de17cff1acaeccc3ff30cd99a3fd5c50fc58ad3d6c0e0c4c58092b859396 \ + --hash=sha256:0f84af7e813784feb4d5e4ff7db633aba6c8ca64a833f61d8e4eade234ef0c38 \ + --hash=sha256:17b2aea42a7280db02ac644db1d634ad47dcc96faf38ab304fe26ba2680d359a \ + --hash=sha256:242d6860f1fd9191aef5fae22b51c5c19767f93fb9ead4d21924e0bcb17619d8 \ + --hash=sha256:244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b \ + --hash=sha256:26627785a54a947f6d7336ce5963569b5d75614619e75193bdb4e06e21d447ad \ + --hash=sha256:2a4b34a8d14649315c4bc26bbfa352663eb51d146e35eef231dd739d54a5430a \ + --hash=sha256:2ae99f31f47d849758a687102afdd05bd3d3ff7dbab0a8f1587981b58a76152a \ + --hash=sha256:312387403cd40699ab91d50735ea7a507b788091c416dd007eac54434aee51da \ + --hash=sha256:3341c043c37d78cc5ae6e3e305e988532b072329639007fd408a476642a89fd6 \ + --hash=sha256:33d1c36b90e570ba7785dacd1faaf091203d9942bc036118fab8110a401eb1a8 \ + --hash=sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344 \ + --hash=sha256:3ffb4a8e7d46ed96ae48805746755fadd0909fea2306f93d5d8233ba23dda12a \ + --hash=sha256:40621d60d0e58aa573b68ac5e2d6b20d44392878e0bfc159012a5787c4e35bc8 \ + --hash=sha256:40f1e10d51c92859765522cbd79c5c8989f40f0419614bcdc5015e7b6bf97fc5 \ + --hash=sha256:45d42d132cff577c92bfba536aefcfea7e26efb975bd455db4e6602f5c9f45e7 \ + --hash=sha256:48488d999ed50ba8d38c581d67e496f955821dc183883550a6fbc7f1aefdc170 \ + --hash=sha256:4935dd7883f1d50e2ffecca0aa33dc1946a94c8f3fdafb8df5c330e48f71b132 \ + --hash=sha256:4c2d64fdba74ad16138300815cfdc6ab2f4647e23ced81f59e940d7d4a1469d9 \ + --hash=sha256:4c8817557d0de9349109acb38b9dd570b03cc5014e8aabf1cbddc6e81005becd \ + --hash=sha256:4ffaaac913c3f7345579db4f33b0020db693f302ca5137f106060316761beea9 \ + --hash=sha256:5a4cb365cb49b750bdb60b846b0c0bc49ed62e59a76635095a179d440540c346 \ + --hash=sha256:62fada2c942702ef8952754abfc1a9f7658a4d5460fabe95ac7ec2cbe0d02abc \ + --hash=sha256:67c519635a4f64e495c50e3107d9b4075aec33634272b5db1cde839e07367589 \ + --hash=sha256:6a54c43d3ec4cf2a39f4387ad044221c66a376e58c0d0e971d47c475ba79c6b5 \ + --hash=sha256:7044312a928a66a4c2a22644147bc61a199c1709712069a344a3fb5cfcf16915 \ + --hash=sha256:730d86af59e0e43ce277bb83970530dd223bf7f2a838e086b50affa6ec5f9295 \ + --hash=sha256:800100d45176652ded796134277ecb13640c1a537cad3b8b53da45aa96330453 \ + --hash=sha256:80fcbf3add8790caddfab6764bde258b5d09aefbe9169c183f88a7410f0f6dea \ + --hash=sha256:82b5dba6eb1bcc29cc305a18a3c5365d2af06ee71b123216416f7e20d2a84e5b \ + --hash=sha256:852dc840f6d7c985603e60b5deaae1d89c56cb038b577f6b5b8c808c97580f1d \ + --hash=sha256:8ad4ad1429cd4f315f32ef263c1342166695fad76c100c5d979c45d5570ed58b \ + --hash=sha256:8ae369e84466aa70f3154ee23c1451fda10a8ee1b63923ce76667e3077f2b0c4 \ + --hash=sha256:93e8248d650e7e9d49e8251f883eed60ecbc0e8ffd6349e18550925e31bd029b \ + --hash=sha256:973a371a55ce9ed333a3a0f8e0bcfae9e0d637711534bcb11e130af2ab9334e7 \ + --hash=sha256:9ba25a71ebf05b9bb0e2ae99f8bc08a07ee8e98c612175087112656ca0f5c8bf \ + --hash=sha256:a10860e00ded1dd0a65b83e717af28845bb7bd16d8ace40fe5531491de76b79f \ + --hash=sha256:a4792d3b3a6dfafefdf8e937f14906a51bd27025a36f4b188728a73382231d91 \ + --hash=sha256:a7420ceda262dbb4b8d839a4ec63d61c261e4e77677ed7c66c99f4e7cb5030dd \ + --hash=sha256:ad91738f14eb8da0ff82f2acd0098b6257621410dcbd4df20aaa5b4233d75a50 \ + --hash=sha256:b6a387d61fe41cdf7ea95b38e9af11cfb1a63499af2759444b99185c4ab33f5b \ + --hash=sha256:b954093679d5750495725ea6f88409946d69cfb25ea7b4c846eef5044194f583 \ + --hash=sha256:bbde71a705f8e9e4c3e9e33db69341d040c827c7afa6789b14c6e16776074f5a \ + --hash=sha256:beeebf760a9c1f4c07ef6a53465e8cfa776ea6a2021eda0d0417ec41043fe984 \ + --hash=sha256:c91b394f7601438ff79a4b93d16be92f216adb57d813a78be4446fe0f6bc2d8c \ + --hash=sha256:c97ff7fedf56d86bae92fa0a646ce1a0ec7509a7578e1ed238731ba13aabcd1c \ + --hash=sha256:cb53e2a99df28eee3b5f4fea166020d3ef9116fdc5764bc5117486e6d1211b25 \ + --hash=sha256:cbf445eb5628981a80f54087f9acdbf84f9b7d862756110d172993b9a5ae81aa \ + --hash=sha256:d06b24c686a34c86c8c1fba923181eae6b10565e4d80bdd7bc1c8e2f11247aa4 \ + --hash=sha256:d98e66a24497637dd31ccab090b34392dddb1f2f811c4b4cd80c230205c074a3 \ + --hash=sha256:db15ce28e1e127a0013dfb8ac243a8e392db8c61eae113337536edb28bdc1f97 \ + --hash=sha256:db842712984e91707437461930e6011e60b39136c7331e971952bb30465bc1a1 \ + --hash=sha256:e24bfe89c6ac4c31792793ad9f861b8f6dc4546ac6dc8f1c9083c7c4f2b335cd \ + --hash=sha256:e81c52638315ff4ac1b533d427f50bc0afc746deb949210bc85f05d4f15fd772 \ + --hash=sha256:e9393357f19954248b00bed7c56f29a25c930593a77630c719653d51e7669c2a \ + --hash=sha256:ee3941769bd2522fe39222206f6dd97ae83c442a94c90f2b7a25d847d40f4729 \ + --hash=sha256:f31ae06f1328595d762c9a2bf29dafd8621c7d3adc130cbb46278079758779ca \ + --hash=sha256:f94190df587738280d544971500b9cafc9b950d32efcb1fba9ac10d84e6aa4e6 \ + --hash=sha256:fa7d686ed9883f3d664d39d5a8e74d3c5f63e603c2e3ff0abcba23eac6542635 \ + --hash=sha256:fb532dd9900381d2e8f48172ddc5a59db4c445a11b9fab40b3b786da40d3b56b \ + --hash=sha256:fe32482b37b4b00c7a52a07211b479653b7fe4f22b2e481b9a9b099d8a430f2f # via jinja2 mdurl==0.1.2 \ --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba # via markdown-it-py -more-itertools==10.3.0 \ - --hash=sha256:e5d93ef411224fbcef366a6e8ddc4c5781bc6359d43412a65dd5964e46111463 \ - --hash=sha256:ea6a02e24a9161e51faad17a8782b92a0df82c12c1c8886fec7f0c3fa1a1b320 +more-itertools==10.5.0 \ + --hash=sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef \ + --hash=sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6 # via # jaraco-classes # jaraco-functools @@ -389,9 +359,9 @@ nh3==0.2.18 \ --hash=sha256:de3ceed6e661954871d6cd78b410213bdcb136f79aafe22aa7182e028b8c7307 \ --hash=sha256:f0eca9ca8628dbb4e916ae2491d72957fdd35f7a5d326b7032a345f111ac07fe # via readme-renderer -nox==2024.4.15 \ - --hash=sha256:6492236efa15a460ecb98e7b67562a28b70da006ab0be164e8821177577c0565 \ - --hash=sha256:ecf6700199cdfa9e5ea0a41ff5e6ef4641d09508eda6edb89d9987864115817f +nox==2024.10.9 \ + --hash=sha256:1d36f309a0a2a853e9bccb76bbef6bb118ba92fa92674d15604ca99adeb29eab \ + --hash=sha256:7aa9dc8d1c27e9f45ab046ffd1c3b2c4f7c91755304769df231308849ebded95 # via -r requirements.in packaging==24.1 \ --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ @@ -403,41 +373,41 @@ pkginfo==1.10.0 \ --hash=sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297 \ --hash=sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097 # via twine -platformdirs==4.2.2 \ - --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \ - --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3 +platformdirs==4.3.6 \ + --hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \ + --hash=sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb # via virtualenv proto-plus==1.24.0 \ --hash=sha256:30b72a5ecafe4406b0d339db35b56c4059064e69227b8c3bda7462397f966445 \ --hash=sha256:402576830425e5f6ce4c2a6702400ac79897dab0b4343821aa5188b0fab81a12 # via google-api-core -protobuf==5.27.2 \ - --hash=sha256:0e341109c609749d501986b835f667c6e1e24531096cff9d34ae411595e26505 \ - --hash=sha256:176c12b1f1c880bf7a76d9f7c75822b6a2bc3db2d28baa4d300e8ce4cde7409b \ - --hash=sha256:354d84fac2b0d76062e9b3221f4abbbacdfd2a4d8af36bab0474f3a0bb30ab38 \ - --hash=sha256:4fadd8d83e1992eed0248bc50a4a6361dc31bcccc84388c54c86e530b7f58863 \ - --hash=sha256:54330f07e4949d09614707c48b06d1a22f8ffb5763c159efd5c0928326a91470 \ - --hash=sha256:610e700f02469c4a997e58e328cac6f305f649826853813177e6290416e846c6 \ - --hash=sha256:7fc3add9e6003e026da5fc9e59b131b8f22b428b991ccd53e2af8071687b4fce \ - --hash=sha256:9e8f199bf7f97bd7ecebffcae45ebf9527603549b2b562df0fbc6d4d688f14ca \ - --hash=sha256:a109916aaac42bff84702fb5187f3edadbc7c97fc2c99c5ff81dd15dcce0d1e5 \ - --hash=sha256:b848dbe1d57ed7c191dfc4ea64b8b004a3f9ece4bf4d0d80a367b76df20bf36e \ - --hash=sha256:f3ecdef226b9af856075f28227ff2c90ce3a594d092c39bee5513573f25e2714 +protobuf==5.28.2 \ + --hash=sha256:2c69461a7fcc8e24be697624c09a839976d82ae75062b11a0972e41fd2cd9132 \ + --hash=sha256:35cfcb15f213449af7ff6198d6eb5f739c37d7e4f1c09b5d0641babf2cc0c68f \ + --hash=sha256:52235802093bd8a2811abbe8bf0ab9c5f54cca0a751fdd3f6ac2a21438bffece \ + --hash=sha256:59379674ff119717404f7454647913787034f03fe7049cbef1d74a97bb4593f0 \ + --hash=sha256:5e8a95246d581eef20471b5d5ba010d55f66740942b95ba9b872d918c459452f \ + --hash=sha256:87317e9bcda04a32f2ee82089a204d3a2f0d3c8aeed16568c7daf4756e4f1fe0 \ + --hash=sha256:8ddc60bf374785fb7cb12510b267f59067fa10087325b8e1855b898a0d81d276 \ + --hash=sha256:a8b9403fc70764b08d2f593ce44f1d2920c5077bf7d311fefec999f8c40f78b7 \ + --hash=sha256:c0ea0123dac3399a2eeb1a1443d82b7afc9ff40241433296769f7da42d142ec3 \ + --hash=sha256:ca53faf29896c526863366a52a8f4d88e69cd04ec9571ed6082fa117fac3ab36 \ + --hash=sha256:eeea10f3dc0ac7e6b4933d32db20662902b4ab81bf28df12218aa389e9c2102d # via # gcp-docuploader # gcp-releasetool # google-api-core # googleapis-common-protos # proto-plus -pyasn1==0.6.0 \ - --hash=sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c \ - --hash=sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473 +pyasn1==0.6.1 \ + --hash=sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629 \ + --hash=sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034 # via # pyasn1-modules # rsa -pyasn1-modules==0.4.0 \ - --hash=sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6 \ - --hash=sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b +pyasn1-modules==0.4.1 \ + --hash=sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd \ + --hash=sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c # via google-auth pycparser==2.22 \ --hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \ @@ -449,9 +419,9 @@ pygments==2.18.0 \ # via # readme-renderer # rich -pyjwt==2.8.0 \ - --hash=sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de \ - --hash=sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320 +pyjwt==2.9.0 \ + --hash=sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850 \ + --hash=sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c # via gcp-releasetool pyperclip==1.9.0 \ --hash=sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310 @@ -481,9 +451,9 @@ rfc3986==2.0.0 \ --hash=sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd \ --hash=sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c # via twine -rich==13.7.1 \ - --hash=sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222 \ - --hash=sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432 +rich==13.9.2 \ + --hash=sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c \ + --hash=sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1 # via twine rsa==4.9 \ --hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \ @@ -499,9 +469,9 @@ six==1.16.0 \ # via # gcp-docuploader # python-dateutil -tomli==2.0.1 \ - --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ - --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f +tomli==2.0.2 \ + --hash=sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38 \ + --hash=sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed # via nox twine==5.1.1 \ --hash=sha256:215dbe7b4b94c2c50a7315c0275d2258399280fbb7d04182c7e55e24b5f93997 \ @@ -510,28 +480,30 @@ twine==5.1.1 \ typing-extensions==4.12.2 \ --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8 - # via -r requirements.in -urllib3==2.2.2 \ - --hash=sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472 \ - --hash=sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168 + # via + # -r requirements.in + # rich +urllib3==2.2.3 \ + --hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac \ + --hash=sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9 # via # requests # twine -virtualenv==20.26.3 \ - --hash=sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a \ - --hash=sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589 +virtualenv==20.26.6 \ + --hash=sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48 \ + --hash=sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2 # via nox -wheel==0.43.0 \ - --hash=sha256:465ef92c69fa5c5da2d1cf8ac40559a8c940886afcef87dcf14b9470862f1d85 \ - --hash=sha256:55c570405f142630c6b9f72fe09d9b67cf1477fcf543ae5b8dcb1f5b7377da81 +wheel==0.44.0 \ + --hash=sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f \ + --hash=sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49 # via -r requirements.in -zipp==3.19.2 \ - --hash=sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19 \ - --hash=sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c +zipp==3.20.2 \ + --hash=sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350 \ + --hash=sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: -setuptools==70.2.0 \ - --hash=sha256:b8b8060bb426838fbe942479c90296ce976249451118ef566a5a0b7d8b78fb05 \ - --hash=sha256:bd63e505105011b25c3c11f753f7e3b8465ea739efddaccef8f0efac2137bac1 +setuptools==75.1.0 \ + --hash=sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2 \ + --hash=sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538 # via -r requirements.in diff --git a/.kokoro/samples/python3.13/common.cfg b/.kokoro/samples/python3.13/common.cfg new file mode 100644 index 000000000..96783769b --- /dev/null +++ b/.kokoro/samples/python3.13/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.13" +} + +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-313" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-pubsub/.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-pubsub/.kokoro/trampoline_v2.sh" diff --git a/.kokoro/samples/python3.13/continuous.cfg b/.kokoro/samples/python3.13/continuous.cfg new file mode 100644 index 000000000..a1c8d9759 --- /dev/null +++ b/.kokoro/samples/python3.13/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.13/periodic-head.cfg b/.kokoro/samples/python3.13/periodic-head.cfg new file mode 100644 index 000000000..f9cfcd33e --- /dev/null +++ b/.kokoro/samples/python3.13/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-pubsub/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/samples/python3.13/periodic.cfg b/.kokoro/samples/python3.13/periodic.cfg new file mode 100644 index 000000000..71cd1e597 --- /dev/null +++ b/.kokoro/samples/python3.13/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.13/presubmit.cfg b/.kokoro/samples/python3.13/presubmit.cfg new file mode 100644 index 000000000..a1c8d9759 --- /dev/null +++ b/.kokoro/samples/python3.13/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/.kokoro/test-samples-impl.sh b/.kokoro/test-samples-impl.sh index 55910c8ba..53e365bc4 100755 --- a/.kokoro/test-samples-impl.sh +++ b/.kokoro/test-samples-impl.sh @@ -33,7 +33,8 @@ export PYTHONUNBUFFERED=1 env | grep KOKORO # Install nox -python3.9 -m pip install --upgrade --quiet nox +# `virtualenv==20.26.6` is added for Python 3.7 compatibility +python3.9 -m pip install --upgrade --quiet nox virtualenv==20.26.6 # Use secrets acessor service account to get secrets if [[ -f "${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" ]]; then diff --git a/noxfile.py b/noxfile.py index c6d0c11a3..1ba75e6b2 100644 --- a/noxfile.py +++ b/noxfile.py @@ -67,7 +67,6 @@ CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() -# 'docfx' is excluded since it only needs to run in 'docs-presubmit' nox.options.sessions = [ "unit", "system", @@ -79,6 +78,7 @@ # https://github.com/googleapis/python-pubsub/pull/552#issuecomment-1016256936 # "mypy_samples", # TODO: uncomment when the check passes "docs", + "docfx", "format", ] @@ -222,7 +222,7 @@ def install_unittest_dependencies(session, *constraints): def unit(session, protobuf_implementation): # Install all test dependencies, then install this package in-place. - if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12"): + if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12", "3.13"): session.skip("cpp implementation is not supported in python 3.11+") constraints_path = str( @@ -430,7 +430,7 @@ def docfx(session): def prerelease_deps(session, protobuf_implementation): """Run all tests with prerelease versions of dependencies installed.""" - if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12"): + if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12", "3.13"): session.skip("cpp implementation is not supported in python 3.11+") # Install all dependencies diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 3b7135946..c9a3d1ecb 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/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", "3.12"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] From d6635a00dc2c614dd8608ef32ad4e79f9124e040 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Sat, 2 Nov 2024 13:32:35 -0400 Subject: [PATCH 112/190] fix: Mark test_streaming_pull_max_messages flaky (#1288) --- tests/system.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/system.py b/tests/system.py index 6bf8ef10f..7b0c0f93a 100644 --- a/tests/system.py +++ b/tests/system.py @@ -46,7 +46,7 @@ from test_utils.system import unique_resource_id C = TypeVar("C", bound=Callable[..., Any]) -typed_flaky = cast(Callable[[C], C], flaky(max_runs=3, min_passes=1)) +typed_flaky = cast(Callable[[C], C], flaky(max_runs=5, min_passes=1)) @pytest.fixture(scope="module") @@ -616,6 +616,7 @@ def test_streaming_pull_ack_deadline( finally: subscription_future.cancel() + @typed_flaky def test_streaming_pull_max_messages( self, publisher, topic_path_base, subscription_path_base, cleanup ): From 0b46a3321d6f19cd72e4f2ccdba73d062c7bd832 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Sat, 2 Nov 2024 16:39:21 -0400 Subject: [PATCH 113/190] feat: Add support for Python 3.13 (#1281) Co-authored-by: Owl Bot Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> --- .github/.OwlBot.lock.yaml | 2 +- .github/workflows/unittest.yml | 2 +- CONTRIBUTING.rst | 6 ++++-- noxfile.py | 12 ++++++++++-- owlbot.py | 2 +- setup.py | 1 + testing/constraints-3.13.txt | 7 +++++++ 7 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 testing/constraints-3.13.txt diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 7672b49b6..862cfa288 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -14,4 +14,4 @@ docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest digest: sha256:5cddfe2fb5019bbf78335bc55f15bc13e18354a56b3ff46e1834f8e540807f05 -# created: 2024-10-31T01:41:07.349286254Z + diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index dd8bd7692..6a0bc0743 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] + python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] steps: - name: Checkout uses: actions/checkout@v4 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 727b5ec7f..f153c3ae7 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -22,7 +22,7 @@ In order to add a feature: documentation. - The feature must work fully on the following CPython versions: - 3.7, 3.8, 3.9, 3.10, 3.11 and 3.12 on both UNIX and Windows. + 3.7, 3.8, 3.9, 3.10, 3.11, 3.12 and 3.13 on both UNIX and Windows. - The feature must not add unnecessary dependencies (where "unnecessary" is of course subjective, but new dependencies should @@ -72,7 +72,7 @@ We use `nox `__ to instrument our tests. - To run a single unit test:: - $ nox -s unit-3.12 -- -k + $ nox -s unit-3.13 -- -k .. note:: @@ -227,6 +227,7 @@ We support: - `Python 3.10`_ - `Python 3.11`_ - `Python 3.12`_ +- `Python 3.13`_ .. _Python 3.7: https://docs.python.org/3.7/ .. _Python 3.8: https://docs.python.org/3.8/ @@ -234,6 +235,7 @@ We support: .. _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/ +.. _Python 3.13: https://docs.python.org/3.13/ Supported versions can be found in our ``noxfile.py`` `config`_. diff --git a/noxfile.py b/noxfile.py index 1ba75e6b2..2ccbfeae7 100644 --- a/noxfile.py +++ b/noxfile.py @@ -36,7 +36,15 @@ DEFAULT_PYTHON_VERSION = "3.8" -UNIT_TEST_PYTHON_VERSIONS: List[str] = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] +UNIT_TEST_PYTHON_VERSIONS: List[str] = [ + "3.7", + "3.8", + "3.9", + "3.10", + "3.11", + "3.12", + "3.13", +] UNIT_TEST_STANDARD_DEPENDENCIES = [ "mock", "asyncmock", @@ -422,7 +430,7 @@ def docfx(session): ) -@nox.session(python="3.12") +@nox.session(python="3.13") @nox.parametrize( "protobuf_implementation", ["python", "upb", "cpp"], diff --git a/owlbot.py b/owlbot.py index 204b30ba5..2e4b00bc9 100644 --- a/owlbot.py +++ b/owlbot.py @@ -336,7 +336,7 @@ samples=True, cov_level=100, versions=gcp.common.detect_versions(path="./google", default_first=True), - unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"], + unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"], system_test_python_versions=["3.12"], system_test_external_dependencies=["psutil","flaky"], ) diff --git a/setup.py b/setup.py index 8339e1e18..abe06552b 100644 --- a/setup.py +++ b/setup.py @@ -86,6 +86,7 @@ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Operating System :: OS Independent", "Topic :: Internet", ], diff --git a/testing/constraints-3.13.txt b/testing/constraints-3.13.txt new file mode 100644 index 000000000..ad3f0fa58 --- /dev/null +++ b/testing/constraints-3.13.txt @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# This constraints file is required for unit tests. +# List all library dependencies and extras in this file. +google-api-core +proto-plus +protobuf +grpc-google-iam-v1 From cdaf6e9bcd0b918c2ab50fd4a8f987f60cbd610f Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Sat, 2 Nov 2024 14:29:24 -0700 Subject: [PATCH 114/190] chore(main): release 2.27.0 (#1289) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 12 ++++++++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 9c3477bd7..b6aa39c5e 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.26.1" + ".": "2.27.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 64bb863eb..d55c31822 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.27.0](https://github.com/googleapis/python-pubsub/compare/v2.26.1...v2.27.0) (2024-11-02) + + +### Features + +* Add support for Python 3.13 ([#1281](https://github.com/googleapis/python-pubsub/issues/1281)) ([0b46a33](https://github.com/googleapis/python-pubsub/commit/0b46a3321d6f19cd72e4f2ccdba73d062c7bd832)) + + +### Bug Fixes + +* Mark test_streaming_pull_max_messages flaky ([#1288](https://github.com/googleapis/python-pubsub/issues/1288)) ([d6635a0](https://github.com/googleapis/python-pubsub/commit/d6635a00dc2c614dd8608ef32ad4e79f9124e040)) + ## [2.26.1](https://github.com/googleapis/python-pubsub/compare/v2.26.0...v2.26.1) (2024-10-10) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 040d4e7f1..f0fcebfa4 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.26.1" # {x-release-please-version} +__version__ = "2.27.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 040d4e7f1..f0fcebfa4 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.26.1" # {x-release-please-version} +__version__ = "2.27.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index ead6d83f3..b1aab3038 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.26.1" + "version": "2.27.0" }, "snippets": [ { From 5c78ac961199cd094ac4d29634fa4b6bfb73436a Mon Sep 17 00:00:00 2001 From: ohmayr Date: Tue, 5 Nov 2024 16:02:04 -0500 Subject: [PATCH 115/190] test: add type flaky to unit test (#1297) Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- noxfile.py | 4 +++- owlbot.py | 1 + setup.py | 2 +- tests/unit/pubsub_v1/publisher/test_publisher_client.py | 7 +++++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/noxfile.py b/noxfile.py index 2ccbfeae7..47611cdff 100644 --- a/noxfile.py +++ b/noxfile.py @@ -55,7 +55,9 @@ UNIT_TEST_EXTERNAL_DEPENDENCIES: List[str] = [] UNIT_TEST_LOCAL_DEPENDENCIES: List[str] = [] UNIT_TEST_DEPENDENCIES: List[str] = [] -UNIT_TEST_EXTRAS: List[str] = [] +UNIT_TEST_EXTRAS: List[str] = [ + "flaky", +] UNIT_TEST_EXTRAS_BY_PYTHON: Dict[str, List[str]] = {} SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.12"] diff --git a/owlbot.py b/owlbot.py index 2e4b00bc9..77eb08250 100644 --- a/owlbot.py +++ b/owlbot.py @@ -337,6 +337,7 @@ cov_level=100, versions=gcp.common.detect_versions(path="./google", default_first=True), unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"], + unit_test_extras=["flaky"], system_test_python_versions=["3.12"], system_test_external_dependencies=["psutil","flaky"], ) diff --git a/setup.py b/setup.py index abe06552b..fb2b94fad 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,7 @@ "opentelemetry-sdk <= 1.22.0; python_version<='3.7'", "opentelemetry-sdk >= 1.27.0; python_version>='3.8'", ] -extras = {"libcst": "libcst >= 0.3.10"} +extras = {"libcst": "libcst >= 0.3.10,", "flaky": "flaky"} url = "https://github.com/googleapis/python-pubsub" package_root = os.path.abspath(os.path.dirname(__file__)) diff --git a/tests/unit/pubsub_v1/publisher/test_publisher_client.py b/tests/unit/pubsub_v1/publisher/test_publisher_client.py index abc33f8cb..55198b590 100644 --- a/tests/unit/pubsub_v1/publisher/test_publisher_client.py +++ b/tests/unit/pubsub_v1/publisher/test_publisher_client.py @@ -28,6 +28,8 @@ import pytest import time +from flaky import flaky +from typing import cast, Callable, Any, TypeVar from opentelemetry import trace from google.api_core import gapic_v1 @@ -49,6 +51,10 @@ ) +C = TypeVar("C", bound=Callable[..., Any]) +typed_flaky = cast(Callable[[C], C], flaky(max_runs=5, min_passes=1)) + + def _assert_retries_equal(retry, retry2): # Retry instances cannot be directly compared, because their predicates are # different instances of the same function. We thus manually compare their other @@ -142,6 +148,7 @@ def test_init_w_custom_transport(creds): False, ], ) +@typed_flaky def test_open_telemetry_publisher_options(creds, enable_open_telemetry): if sys.version_info >= (3, 8) or enable_open_telemetry is False: client = publisher.Client( From 347cb38b37a19ee9cbeeede9e16b65f4e1b2ce4b Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Wed, 6 Nov 2024 16:35:53 -0500 Subject: [PATCH 116/190] chore: partial revert of #1297 (#1298) Co-authored-by: Owl Bot --- noxfile.py | 4 ++-- owlbot.py | 2 +- setup.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/noxfile.py b/noxfile.py index 47611cdff..7bae0161f 100644 --- a/noxfile.py +++ b/noxfile.py @@ -54,10 +54,10 @@ ] UNIT_TEST_EXTERNAL_DEPENDENCIES: List[str] = [] UNIT_TEST_LOCAL_DEPENDENCIES: List[str] = [] -UNIT_TEST_DEPENDENCIES: List[str] = [] -UNIT_TEST_EXTRAS: List[str] = [ +UNIT_TEST_DEPENDENCIES: List[str] = [ "flaky", ] +UNIT_TEST_EXTRAS: List[str] = [] UNIT_TEST_EXTRAS_BY_PYTHON: Dict[str, List[str]] = {} SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.12"] diff --git a/owlbot.py b/owlbot.py index 77eb08250..5828fbecf 100644 --- a/owlbot.py +++ b/owlbot.py @@ -337,7 +337,7 @@ cov_level=100, versions=gcp.common.detect_versions(path="./google", default_first=True), unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"], - unit_test_extras=["flaky"], + unit_test_dependencies=["flaky"], system_test_python_versions=["3.12"], system_test_external_dependencies=["psutil","flaky"], ) diff --git a/setup.py b/setup.py index fb2b94fad..abe06552b 100644 --- a/setup.py +++ b/setup.py @@ -50,7 +50,7 @@ "opentelemetry-sdk <= 1.22.0; python_version<='3.7'", "opentelemetry-sdk >= 1.27.0; python_version>='3.8'", ] -extras = {"libcst": "libcst >= 0.3.10,", "flaky": "flaky"} +extras = {"libcst": "libcst >= 0.3.10"} url = "https://github.com/googleapis/python-pubsub" package_root = os.path.abspath(os.path.dirname(__file__)) From ab22e27954450b4e06ec98fe2e3458056aa8ca60 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Fri, 8 Nov 2024 06:20:25 -0500 Subject: [PATCH 117/190] fix: Add support for Python3.13 (#1302) --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index abe06552b..1d68bf87b 100644 --- a/setup.py +++ b/setup.py @@ -42,6 +42,7 @@ "google-api-core[grpc] >= 1.34.0, <3.0.0dev,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,!=2.10.*", "proto-plus >= 1.22.0, <2.0.0dev", "proto-plus >= 1.22.2, <2.0.0dev; python_version>='3.11'", + "proto-plus >= 1.25.0, < 2.0.0dev; python_version >= '3.13'", "protobuf>=3.20.2,<6.0.0dev,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5", "grpc-google-iam-v1 >= 0.12.4, < 1.0.0dev", "grpcio-status >= 1.33.2", From 206689982111a01062c67bd48d9a5f513d2fd8d1 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Sat, 9 Nov 2024 17:55:41 -0500 Subject: [PATCH 118/190] chore(main): release 2.27.1 (#1303) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b6aa39c5e..aa8b67ac9 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.27.0" + ".": "2.27.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index d55c31822..570ee801f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.27.1](https://github.com/googleapis/python-pubsub/compare/v2.27.0...v2.27.1) (2024-11-08) + + +### Bug Fixes + +* Add support for Python3.13 ([#1302](https://github.com/googleapis/python-pubsub/issues/1302)) ([ab22e27](https://github.com/googleapis/python-pubsub/commit/ab22e27954450b4e06ec98fe2e3458056aa8ca60)) + ## [2.27.0](https://github.com/googleapis/python-pubsub/compare/v2.26.1...v2.27.0) (2024-11-02) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index f0fcebfa4..4e3e20aec 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.27.0" # {x-release-please-version} +__version__ = "2.27.1" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index f0fcebfa4..4e3e20aec 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.27.0" # {x-release-please-version} +__version__ = "2.27.1" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index b1aab3038..0800fa5f3 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.27.0" + "version": "2.27.1" }, "snippets": [ { From 4875408d1f90634ee389a3060324ac493d11d8a9 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 15:36:04 -0500 Subject: [PATCH 119/190] chore(python): update dependencies in .kokoro/docker/docs (#1305) Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 ++-- .github/release-trigger.yml | 2 +- .kokoro/docker/docs/requirements.txt | 20 ++++++++++---------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 862cfa288..6301519a9 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:5cddfe2fb5019bbf78335bc55f15bc13e18354a56b3ff46e1834f8e540807f05 - + digest: sha256:2ed982f884312e4883e01b5ab8af8b6935f0216a5a2d82928d273081fc3be562 +# created: 2024-11-12T12:09:45.821174897Z diff --git a/.github/release-trigger.yml b/.github/release-trigger.yml index 4bb79e58e..aa0d30a3e 100644 --- a/.github/release-trigger.yml +++ b/.github/release-trigger.yml @@ -1,2 +1,2 @@ enabled: true -multiScmName: +multiScmName: python-pubsub diff --git a/.kokoro/docker/docs/requirements.txt b/.kokoro/docker/docs/requirements.txt index 66eacc82f..8bb076459 100644 --- a/.kokoro/docker/docs/requirements.txt +++ b/.kokoro/docker/docs/requirements.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.9 +# This file is autogenerated by pip-compile with Python 3.10 # by the following command: # # pip-compile --allow-unsafe --generate-hashes requirements.in @@ -8,9 +8,9 @@ argcomplete==3.5.1 \ --hash=sha256:1a1d148bdaa3e3b93454900163403df41448a248af01b6e849edc5ac08e6c363 \ --hash=sha256:eb1ee355aa2557bd3d0145de7b06b2a45b0ce461e1e7813f5d066039ab4177b4 # via nox -colorlog==6.8.2 \ - --hash=sha256:3e3e079a41feb5a1b64f978b5ea4f46040a94f11f0e8bbb8261e3dbbeca64d44 \ - --hash=sha256:4dcbb62368e2800cb3c5abd348da7e53f6c362dda502ec27c560b2e58a66bd33 +colorlog==6.9.0 \ + --hash=sha256:5906e71acd67cb07a71e779c47c4bcb45fb8c2993eebe9e5adcd6a6f1b283eff \ + --hash=sha256:bfba54a1b93b94f54e1f4fe48395725a3d92fd2a4af702f6bd70946bdc0c6ac2 # via nox distlib==0.3.9 \ --hash=sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87 \ @@ -24,9 +24,9 @@ nox==2024.10.9 \ --hash=sha256:1d36f309a0a2a853e9bccb76bbef6bb118ba92fa92674d15604ca99adeb29eab \ --hash=sha256:7aa9dc8d1c27e9f45ab046ffd1c3b2c4f7c91755304769df231308849ebded95 # via -r requirements.in -packaging==24.1 \ - --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ - --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 +packaging==24.2 \ + --hash=sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759 \ + --hash=sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f # via nox platformdirs==4.3.6 \ --hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \ @@ -36,7 +36,7 @@ tomli==2.0.2 \ --hash=sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38 \ --hash=sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed # via nox -virtualenv==20.26.6 \ - --hash=sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48 \ - --hash=sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2 +virtualenv==20.27.1 \ + --hash=sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba \ + --hash=sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4 # via nox From 50504aed67b83c94d9c02fc8fbb58822bf8d1335 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Tue, 12 Nov 2024 15:36:41 -0500 Subject: [PATCH 120/190] fix: Reduce the code coverage percentage to 99 (#1278) Co-authored-by: Owl Bot --- .github/workflows/unittest.yml | 2 +- noxfile.py | 2 +- owlbot.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index 6a0bc0743..6eca3149c 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -55,4 +55,4 @@ jobs: run: | find .coverage-results -type f -name '*.zip' -exec unzip {} \; coverage combine .coverage-results/**/.coverage* - coverage report --show-missing --fail-under=100 + coverage report --show-missing --fail-under=99 diff --git a/noxfile.py b/noxfile.py index 7bae0161f..dd182f105 100644 --- a/noxfile.py +++ b/noxfile.py @@ -346,7 +346,7 @@ def cover(session): 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", "report", "--show-missing", "--fail-under=99") session.run("coverage", "erase") diff --git a/owlbot.py b/owlbot.py index 5828fbecf..3c91607d3 100644 --- a/owlbot.py +++ b/owlbot.py @@ -334,7 +334,7 @@ templated_files = gcp.CommonTemplates().py_library( microgenerator=True, samples=True, - cov_level=100, + cov_level=99, versions=gcp.common.detect_versions(path="./google", default_first=True), unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"], unit_test_dependencies=["flaky"], From d4cfc2d1fd7e00d44d4347a359935c149ca758b0 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 3 Jan 2025 09:49:08 -0500 Subject: [PATCH 121/190] chore(python): Update the python version in docs presubmit to use 3.10 (#1315) Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 6 ++-- .github/workflows/docs.yml | 2 +- .github/workflows/unittest.yml | 5 ++- .kokoro/docker/docs/requirements.txt | 52 ++++++++++++++++++++++------ 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 6301519a9..1d0fd7e78 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -1,4 +1,4 @@ -# Copyright 2024 Google LLC +# Copyright 2025 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:2ed982f884312e4883e01b5ab8af8b6935f0216a5a2d82928d273081fc3be562 -# created: 2024-11-12T12:09:45.821174897Z + digest: sha256:a1c5112b81d645f5bbc4d4bbc99d7dcb5089a52216c0e3fb1203a0eeabadd7d5 +# created: 2025-01-02T23:09:36.975468657Z diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 698fbc5c9..2833fe98f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -12,7 +12,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 with: - python-version: "3.9" + python-version: "3.10" - name: Install nox run: | python -m pip install --upgrade setuptools pip wheel diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index 6eca3149c..6a0429d96 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -5,7 +5,10 @@ on: name: unittest jobs: unit: - runs-on: ubuntu-latest + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2303): use `ubuntu-latest` once this bug is fixed. + # Use ubuntu-22.04 until Python 3.7 is removed from the test matrix + # https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories + runs-on: ubuntu-22.04 strategy: matrix: python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] diff --git a/.kokoro/docker/docs/requirements.txt b/.kokoro/docker/docs/requirements.txt index 8bb076459..f99a5c4aa 100644 --- a/.kokoro/docker/docs/requirements.txt +++ b/.kokoro/docker/docs/requirements.txt @@ -2,11 +2,11 @@ # This file is autogenerated by pip-compile with Python 3.10 # by the following command: # -# pip-compile --allow-unsafe --generate-hashes requirements.in +# pip-compile --allow-unsafe --generate-hashes synthtool/gcp/templates/python_library/.kokoro/docker/docs/requirements.in # -argcomplete==3.5.1 \ - --hash=sha256:1a1d148bdaa3e3b93454900163403df41448a248af01b6e849edc5ac08e6c363 \ - --hash=sha256:eb1ee355aa2557bd3d0145de7b06b2a45b0ce461e1e7813f5d066039ab4177b4 +argcomplete==3.5.2 \ + --hash=sha256:036d020d79048a5d525bc63880d7a4b8d1668566b8a76daf1144c0bbe0f63472 \ + --hash=sha256:23146ed7ac4403b70bd6026402468942ceba34a6732255b9edf5b7354f68a6bb # via nox colorlog==6.9.0 \ --hash=sha256:5906e71acd67cb07a71e779c47c4bcb45fb8c2993eebe9e5adcd6a6f1b283eff \ @@ -23,7 +23,7 @@ filelock==3.16.1 \ nox==2024.10.9 \ --hash=sha256:1d36f309a0a2a853e9bccb76bbef6bb118ba92fa92674d15604ca99adeb29eab \ --hash=sha256:7aa9dc8d1c27e9f45ab046ffd1c3b2c4f7c91755304769df231308849ebded95 - # via -r requirements.in + # via -r synthtool/gcp/templates/python_library/.kokoro/docker/docs/requirements.in packaging==24.2 \ --hash=sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759 \ --hash=sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f @@ -32,11 +32,41 @@ platformdirs==4.3.6 \ --hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \ --hash=sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb # via virtualenv -tomli==2.0.2 \ - --hash=sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38 \ - --hash=sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed +tomli==2.2.1 \ + --hash=sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6 \ + --hash=sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd \ + --hash=sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c \ + --hash=sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b \ + --hash=sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8 \ + --hash=sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6 \ + --hash=sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77 \ + --hash=sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff \ + --hash=sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea \ + --hash=sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192 \ + --hash=sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249 \ + --hash=sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee \ + --hash=sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4 \ + --hash=sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98 \ + --hash=sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8 \ + --hash=sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4 \ + --hash=sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281 \ + --hash=sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744 \ + --hash=sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69 \ + --hash=sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13 \ + --hash=sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140 \ + --hash=sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e \ + --hash=sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e \ + --hash=sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc \ + --hash=sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff \ + --hash=sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec \ + --hash=sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2 \ + --hash=sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222 \ + --hash=sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106 \ + --hash=sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272 \ + --hash=sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a \ + --hash=sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7 # via nox -virtualenv==20.27.1 \ - --hash=sha256:142c6be10212543b32c6c45d3d3893dff89112cc588b7d0879ae5a1ec03a47ba \ - --hash=sha256:f11f1b8a29525562925f745563bfd48b189450f61fb34c4f9cc79dd5aa32a1f4 +virtualenv==20.28.0 \ + --hash=sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0 \ + --hash=sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa # via nox From f79d35a21a0e005613f1f80f8ad285da5af09abb Mon Sep 17 00:00:00 2001 From: Zion Amsalem Date: Fri, 3 Jan 2025 17:43:51 +0200 Subject: [PATCH 122/190] tests: Resolve connections deprecation warning (#1310) Co-authored-by: Anthonios Partheniou --- pytest.ini | 2 -- tests/system.py | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pytest.ini b/pytest.ini index 34f393316..4cedf2b4f 100644 --- a/pytest.ini +++ b/pytest.ini @@ -18,5 +18,3 @@ filterwarnings = # Remove once the minimum supported version of googleapis-common-protos is 1.62.0 ignore:.*pkg_resources.declare_namespace:DeprecationWarning ignore:.*pkg_resources is deprecated as an API:DeprecationWarning - # Remove once https://github.com/googleapis/python-pubsub/issues/1206 is fixed. - ignore:.*connections\(\) is deprecated and will be removed; use net_connections\(\) instead:DeprecationWarning \ No newline at end of file diff --git a/tests/system.py b/tests/system.py index 7b0c0f93a..e1af74402 100644 --- a/tests/system.py +++ b/tests/system.py @@ -468,7 +468,7 @@ def test_subscriber_not_leaking_open_sockets( publisher.create_topic(name=topic_path) current_process = psutil.Process() - conn_count_start = len(current_process.connections()) + conn_count_start = len(current_process.net_connections()) # Publish a few messages, then synchronously pull them and check that # no sockets are leaked. @@ -487,7 +487,7 @@ def test_subscriber_not_leaking_open_sockets( response = subscriber.pull(subscription=subscription_path, max_messages=3) assert len(response.received_messages) == 3 - conn_count_end = len(current_process.connections()) + conn_count_end = len(current_process.net_connections()) # To avoid flakiness, use <= in the assertion, since on rare occasions additional # sockets are closed, causing the == assertion to fail. From 0e058c73487384100847adcb2f4ab95a61c072c4 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Mon, 6 Jan 2025 14:35:44 -0500 Subject: [PATCH 123/190] fix: Handle TransportError Exceptions thrown from gapic_publish (#1318) --- google/cloud/pubsub_v1/publisher/_batch/thread.py | 6 +++++- tests/unit/pubsub_v1/publisher/batch/test_thread.py | 13 ++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/google/cloud/pubsub_v1/publisher/_batch/thread.py b/google/cloud/pubsub_v1/publisher/_batch/thread.py index c4bf67c35..2afbe3761 100644 --- a/google/cloud/pubsub_v1/publisher/_batch/thread.py +++ b/google/cloud/pubsub_v1/publisher/_batch/thread.py @@ -24,6 +24,7 @@ from opentelemetry import trace import google.api_core.exceptions from google.api_core import gapic_v1 +from google.auth import exceptions as auth_exceptions from google.cloud.pubsub_v1.publisher import exceptions from google.cloud.pubsub_v1.publisher import futures @@ -342,7 +343,10 @@ def _commit(self) -> None: ) span.set_attribute(key="messaging.message.id", value=message_id) wrapper.end_create_span() - except google.api_core.exceptions.GoogleAPIError as exc: + except ( + google.api_core.exceptions.GoogleAPIError, + auth_exceptions.TransportError, + ) as exc: # We failed to publish, even after retries, so set the exception on # all futures and exit. self._status = base.BatchStatus.ERROR diff --git a/tests/unit/pubsub_v1/publisher/batch/test_thread.py b/tests/unit/pubsub_v1/publisher/batch/test_thread.py index 32eaa3d98..ad8fa376b 100644 --- a/tests/unit/pubsub_v1/publisher/batch/test_thread.py +++ b/tests/unit/pubsub_v1/publisher/batch/test_thread.py @@ -31,6 +31,7 @@ import google.api_core.exceptions from google.api_core import gapic_v1 from google.auth import credentials +from google.auth import exceptions as auth_exceptions from google.cloud.pubsub_v1 import publisher from google.cloud.pubsub_v1 import types from google.cloud.pubsub_v1.publisher import exceptions @@ -329,7 +330,14 @@ def test_blocking__commit_wrong_messageid_length(): assert isinstance(future.exception(), exceptions.PublishError) -def test_block__commmit_api_error(): +@pytest.mark.parametrize( + "error", + [ + (google.api_core.exceptions.InternalServerError("Internal server error"),), + (auth_exceptions.TransportError("some transport error"),), + ], +) +def test_block__commmit_api_error(error): batch = create_batch() futures = ( batch.publish( @@ -345,7 +353,6 @@ def test_block__commmit_api_error(): ) # Make the API throw an error when publishing. - error = google.api_core.exceptions.InternalServerError("uh oh") patch = mock.patch.object(type(batch.client), "_gapic_publish", side_effect=error) with patch: @@ -353,7 +360,7 @@ def test_block__commmit_api_error(): for future in futures: assert future.done() - assert future.exception() == error + assert future.exception() == error[0] def test_block__commmit_retry_error(): From f648f65624d6096908a1e9c4364b36aaf928ce11 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 12:18:57 -0800 Subject: [PATCH 124/190] chore(main): release 2.27.2 (#1319) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index aa8b67ac9..fd23cf2f6 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.27.1" + ".": "2.27.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 570ee801f..bc11c524d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.27.2](https://github.com/googleapis/python-pubsub/compare/v2.27.1...v2.27.2) (2025-01-06) + + +### Bug Fixes + +* Handle TransportError Exceptions thrown from gapic_publish ([#1318](https://github.com/googleapis/python-pubsub/issues/1318)) ([0e058c7](https://github.com/googleapis/python-pubsub/commit/0e058c73487384100847adcb2f4ab95a61c072c4)) + ## [2.27.1](https://github.com/googleapis/python-pubsub/compare/v2.27.0...v2.27.1) (2024-11-08) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 4e3e20aec..d84bd2055 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.27.1" # {x-release-please-version} +__version__ = "2.27.2" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 4e3e20aec..d84bd2055 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.27.1" # {x-release-please-version} +__version__ = "2.27.2" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 0800fa5f3..d96191968 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.27.1" + "version": "2.27.2" }, "snippets": [ { From 8adb133a8caa0744980ffc393331fe4fdda2e3b4 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 11:09:48 -0500 Subject: [PATCH 125/190] chore(python): exclude .github/workflows/unittest.yml in renovate config (#1323) Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 ++-- renovate.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 1d0fd7e78..10cf433a8 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:a1c5112b81d645f5bbc4d4bbc99d7dcb5089a52216c0e3fb1203a0eeabadd7d5 -# created: 2025-01-02T23:09:36.975468657Z + digest: sha256:8ff1efe878e18bd82a0fb7b70bb86f77e7ab6901fed394440b6135db0ba8d84a +# created: 2025-01-09T12:01:16.422459506Z diff --git a/renovate.json b/renovate.json index 39b2a0ec9..c7875c469 100644 --- a/renovate.json +++ b/renovate.json @@ -5,7 +5,7 @@ ":preserveSemverRanges", ":disableDependencyDashboard" ], - "ignorePaths": [".pre-commit-config.yaml", ".kokoro/requirements.txt", "setup.py"], + "ignorePaths": [".pre-commit-config.yaml", ".kokoro/requirements.txt", "setup.py", ".github/workflows/unittest.yml"], "pip_requirements": { "fileMatch": ["requirements-test.txt", "samples/[\\S/]*constraints.txt", "samples/[\\S/]*constraints-test.txt"] } From ba2c2eef7da89a3c14c14d9b6191cd8738c30341 Mon Sep 17 00:00:00 2001 From: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> Date: Fri, 24 Jan 2025 15:16:13 -0500 Subject: [PATCH 126/190] fix: Stop using api_core default timeouts in publish since they are broken (#1326) --- google/cloud/pubsub_v1/types.py | 6 +++++- .../unit/pubsub_v1/publisher/test_publisher_client.py | 10 +++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/google/cloud/pubsub_v1/types.py b/google/cloud/pubsub_v1/types.py index 7e94a7250..6746e141a 100644 --- a/google/cloud/pubsub_v1/types.py +++ b/google/cloud/pubsub_v1/types.py @@ -35,6 +35,7 @@ from google.protobuf import timestamp_pb2 from google.api_core.protobuf_helpers import get_messages +from google.api_core.timeout import ConstantTimeout from google.pubsub_v1.types import pubsub as pubsub_gapic_types @@ -191,7 +192,10 @@ class PublisherOptions(NamedTuple): "an instance of :class:`google.api_core.retry.Retry`." ) - timeout: "OptionalTimeout" = gapic_v1.method.DEFAULT # use api_core default + # Use ConstantTimeout instead of api_core default because the default + # value results in retries with zero deadline. + # Refer https://github.com/googleapis/python-api-core/issues/654 + timeout: "OptionalTimeout" = ConstantTimeout(60) ( "Timeout settings for message publishing by the client. It should be " "compatible with :class:`~.pubsub_v1.types.TimeoutType`." diff --git a/tests/unit/pubsub_v1/publisher/test_publisher_client.py b/tests/unit/pubsub_v1/publisher/test_publisher_client.py index 55198b590..a311edf23 100644 --- a/tests/unit/pubsub_v1/publisher/test_publisher_client.py +++ b/tests/unit/pubsub_v1/publisher/test_publisher_client.py @@ -19,6 +19,7 @@ import sys import grpc +import math # special case python < 3.8 if sys.version_info.major == 3 and sys.version_info.minor < 8: @@ -35,6 +36,7 @@ from google.api_core import gapic_v1 from google.api_core import retry as retries from google.api_core.gapic_v1.client_info import METRICS_METADATA_KEY +from google.api_core.timeout import ConstantTimeout from google.cloud.pubsub_v1 import publisher from google.cloud.pubsub_v1 import types @@ -652,6 +654,8 @@ def test_publish_new_batch_needed(creds): future = client.publish(topic, b"foo", bar=b"baz") assert future is mock.sentinel.future + call_args = batch_class.call_args + # Check the mocks. batch_class.assert_called_once_with( client=mock.ANY, @@ -660,8 +664,12 @@ def test_publish_new_batch_needed(creds): batch_done_callback=None, commit_when_full=True, commit_retry=gapic_v1.method.DEFAULT, - commit_timeout=gapic_v1.method.DEFAULT, + commit_timeout=mock.ANY, ) + commit_timeout_arg = call_args[1]["commit_timeout"] + assert isinstance(commit_timeout_arg, ConstantTimeout) + assert math.isclose(commit_timeout_arg._timeout, 60) is True + message_pb = gapic_types.PubsubMessage(data=b"foo", attributes={"bar": "baz"}) wrapper = PublishMessageWrapper(message=message_pb) batch1.publish.assert_called_once_with(wrapper) From 32d0998e3a70514b433e023e57f07a5ed8fa58a0 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 13:03:42 -0800 Subject: [PATCH 127/190] chore(main): release 2.27.3 (#1350) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index fd23cf2f6..f760fdd07 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.27.2" + ".": "2.27.3" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index bc11c524d..69b1f0c27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.27.3](https://github.com/googleapis/python-pubsub/compare/v2.27.2...v2.27.3) (2025-01-24) + + +### Bug Fixes + +* Stop using api_core default timeouts in publish since they are broken ([#1326](https://github.com/googleapis/python-pubsub/issues/1326)) ([ba2c2ee](https://github.com/googleapis/python-pubsub/commit/ba2c2eef7da89a3c14c14d9b6191cd8738c30341)) + ## [2.27.2](https://github.com/googleapis/python-pubsub/compare/v2.27.1...v2.27.2) (2025-01-06) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index d84bd2055..03a0c3372 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.27.2" # {x-release-please-version} +__version__ = "2.27.3" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index d84bd2055..03a0c3372 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.27.2" # {x-release-please-version} +__version__ = "2.27.3" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index d96191968..f06040919 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.27.2" + "version": "2.27.3" }, "snippets": [ { From 59965a4804a434467a47815cdbdd5ce31bbb3662 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Thu, 30 Jan 2025 01:38:36 +0500 Subject: [PATCH 128/190] fix: set creds only if transport not provided (#1348) Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> --- google/cloud/pubsub_v1/publisher/client.py | 6 +++++- google/cloud/pubsub_v1/subscriber/client.py | 6 +++++- owlbot.py | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/google/cloud/pubsub_v1/publisher/client.py b/google/cloud/pubsub_v1/publisher/client.py index 481a8472d..0740e3185 100644 --- a/google/cloud/pubsub_v1/publisher/client.py +++ b/google/cloud/pubsub_v1/publisher/client.py @@ -128,11 +128,15 @@ def __init__( # Sanity check: Is our goal to use the emulator? # If so, create a grpc insecure channel with the emulator host # as the target. + # TODO(https://github.com/googleapis/python-pubsub/issues/1349): Move the emulator + # code below to test files. if os.environ.get("PUBSUB_EMULATOR_HOST"): kwargs["client_options"] = { "api_endpoint": os.environ.get("PUBSUB_EMULATOR_HOST") } - kwargs["credentials"] = AnonymousCredentials() + # Configure credentials directly to transport, if provided. + if "transport" not in kwargs: + kwargs["credentials"] = AnonymousCredentials() # For a transient failure, retry publishing the message infinitely. self.publisher_options = types.PublisherOptions(*publisher_options) diff --git a/google/cloud/pubsub_v1/subscriber/client.py b/google/cloud/pubsub_v1/subscriber/client.py index 175095077..41277e5e1 100644 --- a/google/cloud/pubsub_v1/subscriber/client.py +++ b/google/cloud/pubsub_v1/subscriber/client.py @@ -81,11 +81,15 @@ def __init__( # Sanity check: Is our goal to use the emulator? # If so, create a grpc insecure channel with the emulator host # as the target. + # TODO(https://github.com/googleapis/python-pubsub/issues/1349): Move the emulator + # code below to test files. if os.environ.get("PUBSUB_EMULATOR_HOST"): kwargs["client_options"] = { "api_endpoint": os.environ.get("PUBSUB_EMULATOR_HOST") } - kwargs["credentials"] = AnonymousCredentials() + # Configure credentials directly to transport, if provided. + if "transport" not in kwargs: + kwargs["credentials"] = AnonymousCredentials() # Instantiate the underlying GAPIC client. super().__init__(**kwargs) diff --git a/owlbot.py b/owlbot.py index 3c91607d3..a8044bd6f 100644 --- a/owlbot.py +++ b/owlbot.py @@ -99,6 +99,8 @@ if count < len(clients_to_patch): raise Exception(err_msg) + # TODO(https://github.com/googleapis/python-pubsub/issues/1349): Move the emulator + # code below to test files. count = s.replace( clients_to_patch, r"# initialize with the provided callable or the passed in class", From 16ea76611d121700a3f3119d18919063d12c81c1 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Thu, 30 Jan 2025 02:53:27 +0500 Subject: [PATCH 129/190] fix: get channel target for a gRPC request (#1339) --- .../publisher/test_publisher_client.py | 24 ++++++++++++++++--- .../subscriber/test_subscriber_client.py | 24 ++++++++++++++++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/tests/unit/pubsub_v1/publisher/test_publisher_client.py b/tests/unit/pubsub_v1/publisher/test_publisher_client.py index a311edf23..1e1cc61b3 100644 --- a/tests/unit/pubsub_v1/publisher/test_publisher_client.py +++ b/tests/unit/pubsub_v1/publisher/test_publisher_client.py @@ -57,6 +57,14 @@ typed_flaky = cast(Callable[[C], C], flaky(max_runs=5, min_passes=1)) +# NOTE: This interceptor is required to create an intercept channel. +class _PublisherClientGrpcInterceptor( + grpc.UnaryUnaryClientInterceptor, +): + def intercept_unary_unary(self, continuation, client_call_details, request): + pass + + def _assert_retries_equal(retry, retry2): # Retry instances cannot be directly compared, because their predicates are # different instances of the same function. We thus manually compare their other @@ -416,17 +424,27 @@ def init(self, *args, **kwargs): assert client.transport._ssl_channel_credentials == mock_ssl_creds -def test_init_emulator(monkeypatch): +def test_init_emulator(monkeypatch, creds): monkeypatch.setenv("PUBSUB_EMULATOR_HOST", "/foo/bar:123") # NOTE: When the emulator host is set, a custom channel will be used, so # no credentials (mock ot otherwise) can be passed in. - client = publisher.Client() + + # TODO(https://github.com/grpc/grpc/issues/38519): Workaround to create an intercept + # channel (for forwards compatibility) with a channel created by the publisher client + # where target is set to the emulator host. + channel = publisher.Client().transport.grpc_channel + interceptor = _PublisherClientGrpcInterceptor() + intercept_channel = grpc.intercept_channel(channel, interceptor) + transport = publisher.Client.get_transport_class("grpc")( + credentials=creds, channel=intercept_channel + ) + client = publisher.Client(transport=transport) # Establish that a gRPC request would attempt to hit the emulator host. # # Sadly, there seems to be no good way to do this without poking at # the private API of gRPC. - channel = client._transport.publish._channel + channel = client._transport.publish._thunk("")._channel # Behavior to include dns prefix changed in gRPCv1.63 grpc_major, grpc_minor = [int(part) for part in grpc.__version__.split(".")[0:2]] if grpc_major > 1 or (grpc_major == 1 and grpc_minor >= 63): diff --git a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py index 7c0ebfd83..4b381245d 100644 --- a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py +++ b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py @@ -36,6 +36,14 @@ from google.pubsub_v1.types import PubsubMessage +# NOTE: This interceptor is required to create an intercept channel. +class _SubscriberClientGrpcInterceptor( + grpc.UnaryUnaryClientInterceptor, +): + def intercept_unary_unary(self, continuation, client_call_details, request): + pass + + def test_init_default_client_info(creds): client = subscriber.Client(credentials=creds) @@ -119,17 +127,27 @@ def init(self, *args, **kwargs): assert client.transport._ssl_channel_credentials == mock_ssl_creds -def test_init_emulator(monkeypatch): +def test_init_emulator(monkeypatch, creds): monkeypatch.setenv("PUBSUB_EMULATOR_HOST", "/baz/bacon:123") # NOTE: When the emulator host is set, a custom channel will be used, so # no credentials (mock ot otherwise) can be passed in. - client = subscriber.Client() + + # TODO(https://github.com/grpc/grpc/issues/38519): Workaround to create an intercept + # channel (for forwards compatibility) with a channel created by the publisher client + # where target is set to the emulator host. + channel = subscriber.Client().transport.grpc_channel + interceptor = _SubscriberClientGrpcInterceptor() + intercept_channel = grpc.intercept_channel(channel, interceptor) + transport = subscriber.Client.get_transport_class("grpc")( + credentials=creds, channel=intercept_channel + ) + client = subscriber.Client(transport=transport) # Establish that a gRPC request would attempt to hit the emulator host. # # Sadly, there seems to be no good way to do this without poking at # the private API of gRPC. - channel = client._transport.pull._channel + channel = client._transport.pull._thunk("")._channel # Behavior to include dns prefix changed in gRPCv1.63 grpc_major, grpc_minor = [int(part) for part in grpc.__version__.split(".")[0:2]] if grpc_major > 1 or (grpc_major == 1 and grpc_minor >= 63): From fc45eb56f8e232f1f91a8aa2e1a877fa81e5ef9b Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Thu, 30 Jan 2025 13:30:24 -0500 Subject: [PATCH 130/190] chore(python): fix docs publish build (#1327) Co-authored-by: Owl Bot --- .github/.OwlBot.lock.yaml | 4 +- .kokoro/docker/docs/requirements.in | 1 + .kokoro/docker/docs/requirements.txt | 243 ++++++++++++++++++++++++++- .kokoro/publish-docs.sh | 4 - 4 files changed, 237 insertions(+), 15 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 10cf433a8..4c0027ff1 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:8ff1efe878e18bd82a0fb7b70bb86f77e7ab6901fed394440b6135db0ba8d84a -# created: 2025-01-09T12:01:16.422459506Z + digest: sha256:04c35dc5f49f0f503a306397d6d043685f8d2bb822ab515818c4208d7fb2db3a +# created: 2025-01-16T15:24:11.364245182Z diff --git a/.kokoro/docker/docs/requirements.in b/.kokoro/docker/docs/requirements.in index 816817c67..586bd0703 100644 --- a/.kokoro/docker/docs/requirements.in +++ b/.kokoro/docker/docs/requirements.in @@ -1 +1,2 @@ nox +gcp-docuploader diff --git a/.kokoro/docker/docs/requirements.txt b/.kokoro/docker/docs/requirements.txt index f99a5c4aa..a9360a25b 100644 --- a/.kokoro/docker/docs/requirements.txt +++ b/.kokoro/docker/docs/requirements.txt @@ -2,16 +2,124 @@ # This file is autogenerated by pip-compile with Python 3.10 # by the following command: # -# pip-compile --allow-unsafe --generate-hashes synthtool/gcp/templates/python_library/.kokoro/docker/docs/requirements.in +# pip-compile --allow-unsafe --generate-hashes requirements.in # -argcomplete==3.5.2 \ - --hash=sha256:036d020d79048a5d525bc63880d7a4b8d1668566b8a76daf1144c0bbe0f63472 \ - --hash=sha256:23146ed7ac4403b70bd6026402468942ceba34a6732255b9edf5b7354f68a6bb +argcomplete==3.5.3 \ + --hash=sha256:2ab2c4a215c59fd6caaff41a869480a23e8f6a5f910b266c1808037f4e375b61 \ + --hash=sha256:c12bf50eded8aebb298c7b7da7a5ff3ee24dffd9f5281867dfe1424b58c55392 # via nox +cachetools==5.5.0 \ + --hash=sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292 \ + --hash=sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a + # via google-auth +certifi==2024.12.14 \ + --hash=sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56 \ + --hash=sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db + # via requests +charset-normalizer==3.4.1 \ + --hash=sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537 \ + --hash=sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa \ + --hash=sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a \ + --hash=sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294 \ + --hash=sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b \ + --hash=sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd \ + --hash=sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601 \ + --hash=sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd \ + --hash=sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4 \ + --hash=sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d \ + --hash=sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2 \ + --hash=sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313 \ + --hash=sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd \ + --hash=sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa \ + --hash=sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8 \ + --hash=sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1 \ + --hash=sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2 \ + --hash=sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496 \ + --hash=sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d \ + --hash=sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b \ + --hash=sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e \ + --hash=sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a \ + --hash=sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4 \ + --hash=sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca \ + --hash=sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78 \ + --hash=sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408 \ + --hash=sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5 \ + --hash=sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3 \ + --hash=sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f \ + --hash=sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a \ + --hash=sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765 \ + --hash=sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6 \ + --hash=sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146 \ + --hash=sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6 \ + --hash=sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9 \ + --hash=sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd \ + --hash=sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c \ + --hash=sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f \ + --hash=sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545 \ + --hash=sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176 \ + --hash=sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770 \ + --hash=sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824 \ + --hash=sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f \ + --hash=sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf \ + --hash=sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487 \ + --hash=sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d \ + --hash=sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd \ + --hash=sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b \ + --hash=sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534 \ + --hash=sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f \ + --hash=sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b \ + --hash=sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9 \ + --hash=sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd \ + --hash=sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125 \ + --hash=sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9 \ + --hash=sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de \ + --hash=sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11 \ + --hash=sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d \ + --hash=sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35 \ + --hash=sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f \ + --hash=sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda \ + --hash=sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7 \ + --hash=sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a \ + --hash=sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971 \ + --hash=sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8 \ + --hash=sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41 \ + --hash=sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d \ + --hash=sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f \ + --hash=sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757 \ + --hash=sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a \ + --hash=sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886 \ + --hash=sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77 \ + --hash=sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76 \ + --hash=sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247 \ + --hash=sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85 \ + --hash=sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb \ + --hash=sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7 \ + --hash=sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e \ + --hash=sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6 \ + --hash=sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037 \ + --hash=sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1 \ + --hash=sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e \ + --hash=sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807 \ + --hash=sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407 \ + --hash=sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c \ + --hash=sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12 \ + --hash=sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3 \ + --hash=sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089 \ + --hash=sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd \ + --hash=sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e \ + --hash=sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00 \ + --hash=sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616 + # via requests +click==8.1.8 \ + --hash=sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2 \ + --hash=sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a + # via gcp-docuploader colorlog==6.9.0 \ --hash=sha256:5906e71acd67cb07a71e779c47c4bcb45fb8c2993eebe9e5adcd6a6f1b283eff \ --hash=sha256:bfba54a1b93b94f54e1f4fe48395725a3d92fd2a4af702f6bd70946bdc0c6ac2 - # via nox + # via + # gcp-docuploader + # nox distlib==0.3.9 \ --hash=sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87 \ --hash=sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403 @@ -20,10 +128,78 @@ filelock==3.16.1 \ --hash=sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0 \ --hash=sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435 # via virtualenv +gcp-docuploader==0.6.5 \ + --hash=sha256:30221d4ac3e5a2b9c69aa52fdbef68cc3f27d0e6d0d90e220fc024584b8d2318 \ + --hash=sha256:b7458ef93f605b9d46a4bf3a8dc1755dad1f31d030c8679edf304e343b347eea + # via -r requirements.in +google-api-core==2.24.0 \ + --hash=sha256:10d82ac0fca69c82a25b3efdeefccf6f28e02ebb97925a8cce8edbfe379929d9 \ + --hash=sha256:e255640547a597a4da010876d333208ddac417d60add22b6851a0c66a831fcaf + # via + # google-cloud-core + # google-cloud-storage +google-auth==2.37.0 \ + --hash=sha256:0054623abf1f9c83492c63d3f47e77f0a544caa3d40b2d98e099a611c2dd5d00 \ + --hash=sha256:42664f18290a6be591be5329a96fe30184be1a1badb7292a7f686a9659de9ca0 + # via + # google-api-core + # google-cloud-core + # google-cloud-storage +google-cloud-core==2.4.1 \ + --hash=sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073 \ + --hash=sha256:a9e6a4422b9ac5c29f79a0ede9485473338e2ce78d91f2370c01e730eab22e61 + # via google-cloud-storage +google-cloud-storage==2.19.0 \ + --hash=sha256:aeb971b5c29cf8ab98445082cbfe7b161a1f48ed275822f59ed3f1524ea54fba \ + --hash=sha256:cd05e9e7191ba6cb68934d8eb76054d9be4562aa89dbc4236feee4d7d51342b2 + # via gcp-docuploader +google-crc32c==1.6.0 \ + --hash=sha256:05e2d8c9a2f853ff116db9706b4a27350587f341eda835f46db3c0a8c8ce2f24 \ + --hash=sha256:18e311c64008f1f1379158158bb3f0c8d72635b9eb4f9545f8cf990c5668e59d \ + --hash=sha256:236c87a46cdf06384f614e9092b82c05f81bd34b80248021f729396a78e55d7e \ + --hash=sha256:35834855408429cecf495cac67ccbab802de269e948e27478b1e47dfb6465e57 \ + --hash=sha256:386122eeaaa76951a8196310432c5b0ef3b53590ef4c317ec7588ec554fec5d2 \ + --hash=sha256:40b05ab32a5067525670880eb5d169529089a26fe35dce8891127aeddc1950e8 \ + --hash=sha256:48abd62ca76a2cbe034542ed1b6aee851b6f28aaca4e6551b5599b6f3ef175cc \ + --hash=sha256:50cf2a96da226dcbff8671233ecf37bf6e95de98b2a2ebadbfdf455e6d05df42 \ + --hash=sha256:51c4f54dd8c6dfeb58d1df5e4f7f97df8abf17a36626a217f169893d1d7f3e9f \ + --hash=sha256:5bcc90b34df28a4b38653c36bb5ada35671ad105c99cfe915fb5bed7ad6924aa \ + --hash=sha256:62f6d4a29fea082ac4a3c9be5e415218255cf11684ac6ef5488eea0c9132689b \ + --hash=sha256:6eceb6ad197656a1ff49ebfbbfa870678c75be4344feb35ac1edf694309413dc \ + --hash=sha256:7aec8e88a3583515f9e0957fe4f5f6d8d4997e36d0f61624e70469771584c760 \ + --hash=sha256:91ca8145b060679ec9176e6de4f89b07363d6805bd4760631ef254905503598d \ + --hash=sha256:a184243544811e4a50d345838a883733461e67578959ac59964e43cca2c791e7 \ + --hash=sha256:a9e4b426c3702f3cd23b933436487eb34e01e00327fac20c9aebb68ccf34117d \ + --hash=sha256:bb0966e1c50d0ef5bc743312cc730b533491d60585a9a08f897274e57c3f70e0 \ + --hash=sha256:bb8b3c75bd157010459b15222c3fd30577042a7060e29d42dabce449c087f2b3 \ + --hash=sha256:bd5e7d2445d1a958c266bfa5d04c39932dc54093fa391736dbfdb0f1929c1fb3 \ + --hash=sha256:c87d98c7c4a69066fd31701c4e10d178a648c2cac3452e62c6b24dc51f9fcc00 \ + --hash=sha256:d2952396dc604544ea7476b33fe87faedc24d666fb0c2d5ac971a2b9576ab871 \ + --hash=sha256:d8797406499f28b5ef791f339594b0b5fdedf54e203b5066675c406ba69d705c \ + --hash=sha256:d9e9913f7bd69e093b81da4535ce27af842e7bf371cde42d1ae9e9bd382dc0e9 \ + --hash=sha256:e2806553238cd076f0a55bddab37a532b53580e699ed8e5606d0de1f856b5205 \ + --hash=sha256:ebab974b1687509e5c973b5c4b8b146683e101e102e17a86bd196ecaa4d099fc \ + --hash=sha256:ed767bf4ba90104c1216b68111613f0d5926fb3780660ea1198fc469af410e9d \ + --hash=sha256:f7a1fc29803712f80879b0806cb83ab24ce62fc8daf0569f2204a0cfd7f68ed4 + # via + # google-cloud-storage + # google-resumable-media +google-resumable-media==2.7.2 \ + --hash=sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa \ + --hash=sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0 + # via google-cloud-storage +googleapis-common-protos==1.66.0 \ + --hash=sha256:c3e7b33d15fdca5374cc0a7346dd92ffa847425cc4ea941d970f13680052ec8c \ + --hash=sha256:d7abcd75fabb2e0ec9f74466401f6c119a0b498e27370e9be4c94cb7e382b8ed + # via google-api-core +idna==3.10 \ + --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ + --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 + # via requests nox==2024.10.9 \ --hash=sha256:1d36f309a0a2a853e9bccb76bbef6bb118ba92fa92674d15604ca99adeb29eab \ --hash=sha256:7aa9dc8d1c27e9f45ab046ffd1c3b2c4f7c91755304769df231308849ebded95 - # via -r synthtool/gcp/templates/python_library/.kokoro/docker/docs/requirements.in + # via -r requirements.in packaging==24.2 \ --hash=sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759 \ --hash=sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f @@ -32,6 +208,51 @@ platformdirs==4.3.6 \ --hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \ --hash=sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb # via virtualenv +proto-plus==1.25.0 \ + --hash=sha256:c91fc4a65074ade8e458e95ef8bac34d4008daa7cce4a12d6707066fca648961 \ + --hash=sha256:fbb17f57f7bd05a68b7707e745e26528b0b3c34e378db91eef93912c54982d91 + # via google-api-core +protobuf==5.29.3 \ + --hash=sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f \ + --hash=sha256:0eb32bfa5219fc8d4111803e9a690658aa2e6366384fd0851064b963b6d1f2a7 \ + --hash=sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888 \ + --hash=sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620 \ + --hash=sha256:6ce8cc3389a20693bfde6c6562e03474c40851b44975c9b2bf6df7d8c4f864da \ + --hash=sha256:84a57163a0ccef3f96e4b6a20516cedcf5bb3a95a657131c5c3ac62200d23252 \ + --hash=sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a \ + --hash=sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e \ + --hash=sha256:b89c115d877892a512f79a8114564fb435943b59067615894c3b13cd3e1fa107 \ + --hash=sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f \ + --hash=sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84 + # via + # gcp-docuploader + # google-api-core + # googleapis-common-protos + # proto-plus +pyasn1==0.6.1 \ + --hash=sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629 \ + --hash=sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034 + # via + # pyasn1-modules + # rsa +pyasn1-modules==0.4.1 \ + --hash=sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd \ + --hash=sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c + # via google-auth +requests==2.32.3 \ + --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \ + --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6 + # via + # google-api-core + # google-cloud-storage +rsa==4.9 \ + --hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \ + --hash=sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21 + # via google-auth +six==1.17.0 \ + --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ + --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 + # via gcp-docuploader tomli==2.2.1 \ --hash=sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6 \ --hash=sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd \ @@ -66,7 +287,11 @@ tomli==2.2.1 \ --hash=sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a \ --hash=sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7 # via nox -virtualenv==20.28.0 \ - --hash=sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0 \ - --hash=sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa +urllib3==2.3.0 \ + --hash=sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df \ + --hash=sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d + # via requests +virtualenv==20.28.1 \ + --hash=sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb \ + --hash=sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329 # via nox diff --git a/.kokoro/publish-docs.sh b/.kokoro/publish-docs.sh index 233205d58..4ed4aaf13 100755 --- a/.kokoro/publish-docs.sh +++ b/.kokoro/publish-docs.sh @@ -20,10 +20,6 @@ export PYTHONUNBUFFERED=1 export PATH="${HOME}/.local/bin:${PATH}" -# Install nox -python3.10 -m pip install --require-hashes -r .kokoro/requirements.txt -python3.10 -m nox --version - # build docs nox -s docs From e5e2f3f732f451d14dfb4c37ae979e5c04045305 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Thu, 30 Jan 2025 14:49:49 -0800 Subject: [PATCH 131/190] feat: add support for message transforms to Topic and Subscription (#1274) Co-authored-by: Owl Bot Co-authored-by: ohmayr Co-authored-by: Anthonios Partheniou --- google/pubsub/__init__.py | 6 + google/pubsub_v1/__init__.py | 6 + .../services/publisher/async_client.py | 20 +- google/pubsub_v1/services/publisher/client.py | 40 +- .../services/publisher/transports/README.rst | 9 + .../services/publisher/transports/base.py | 15 + .../publisher/transports/grpc_asyncio.py | 46 +- .../services/publisher/transports/rest.py | 1042 +-- .../publisher/transports/rest_base.py | 680 ++ .../services/schema_service/async_client.py | 20 +- .../services/schema_service/client.py | 40 +- .../schema_service/transports/README.rst | 9 + .../schema_service/transports/base.py | 15 + .../schema_service/transports/grpc_asyncio.py | 48 +- .../schema_service/transports/rest.py | 1140 +-- .../schema_service/transports/rest_base.py | 748 ++ .../services/subscriber/async_client.py | 20 +- .../pubsub_v1/services/subscriber/client.py | 40 +- .../services/subscriber/transports/README.rst | 9 + .../services/subscriber/transports/base.py | 15 + .../subscriber/transports/grpc_asyncio.py | 60 +- .../services/subscriber/transports/rest.py | 1588 +++-- .../subscriber/transports/rest_base.py | 1026 +++ google/pubsub_v1/types/__init__.py | 6 + google/pubsub_v1/types/pubsub.py | 698 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- scripts/fixup_pubsub_v1_keywords.py | 4 +- tests/unit/gapic/pubsub_v1/test_publisher.py | 3938 ++++++----- .../gapic/pubsub_v1/test_schema_service.py | 4586 ++++++------ tests/unit/gapic/pubsub_v1/test_subscriber.py | 6300 +++++++++-------- 30 files changed, 13099 insertions(+), 9077 deletions(-) create mode 100644 google/pubsub_v1/services/publisher/transports/README.rst create mode 100644 google/pubsub_v1/services/publisher/transports/rest_base.py create mode 100644 google/pubsub_v1/services/schema_service/transports/README.rst create mode 100644 google/pubsub_v1/services/schema_service/transports/rest_base.py create mode 100644 google/pubsub_v1/services/subscriber/transports/README.rst create mode 100644 google/pubsub_v1/services/subscriber/transports/rest_base.py diff --git a/google/pubsub/__init__.py b/google/pubsub/__init__.py index 84f0b8294..c3f034996 100644 --- a/google/pubsub/__init__.py +++ b/google/pubsub/__init__.py @@ -42,6 +42,8 @@ from google.pubsub_v1.types.pubsub import GetSubscriptionRequest from google.pubsub_v1.types.pubsub import GetTopicRequest from google.pubsub_v1.types.pubsub import IngestionDataSourceSettings +from google.pubsub_v1.types.pubsub import IngestionFailureEvent +from google.pubsub_v1.types.pubsub import JavaScriptUDF from google.pubsub_v1.types.pubsub import ListSnapshotsRequest from google.pubsub_v1.types.pubsub import ListSnapshotsResponse from google.pubsub_v1.types.pubsub import ListSubscriptionsRequest @@ -53,6 +55,7 @@ from google.pubsub_v1.types.pubsub import ListTopicSubscriptionsRequest from google.pubsub_v1.types.pubsub import ListTopicSubscriptionsResponse from google.pubsub_v1.types.pubsub import MessageStoragePolicy +from google.pubsub_v1.types.pubsub import MessageTransform from google.pubsub_v1.types.pubsub import ModifyAckDeadlineRequest from google.pubsub_v1.types.pubsub import ModifyPushConfigRequest from google.pubsub_v1.types.pubsub import PlatformLogsSettings @@ -115,6 +118,8 @@ "GetSubscriptionRequest", "GetTopicRequest", "IngestionDataSourceSettings", + "IngestionFailureEvent", + "JavaScriptUDF", "ListSnapshotsRequest", "ListSnapshotsResponse", "ListSubscriptionsRequest", @@ -126,6 +131,7 @@ "ListTopicSubscriptionsRequest", "ListTopicSubscriptionsResponse", "MessageStoragePolicy", + "MessageTransform", "ModifyAckDeadlineRequest", "ModifyPushConfigRequest", "PlatformLogsSettings", diff --git a/google/pubsub_v1/__init__.py b/google/pubsub_v1/__init__.py index fd7ecb6d4..751f77206 100644 --- a/google/pubsub_v1/__init__.py +++ b/google/pubsub_v1/__init__.py @@ -40,6 +40,8 @@ from .types.pubsub import GetSubscriptionRequest from .types.pubsub import GetTopicRequest from .types.pubsub import IngestionDataSourceSettings +from .types.pubsub import IngestionFailureEvent +from .types.pubsub import JavaScriptUDF from .types.pubsub import ListSnapshotsRequest from .types.pubsub import ListSnapshotsResponse from .types.pubsub import ListSubscriptionsRequest @@ -51,6 +53,7 @@ from .types.pubsub import ListTopicSubscriptionsRequest from .types.pubsub import ListTopicSubscriptionsResponse from .types.pubsub import MessageStoragePolicy +from .types.pubsub import MessageTransform from .types.pubsub import ModifyAckDeadlineRequest from .types.pubsub import ModifyPushConfigRequest from .types.pubsub import PlatformLogsSettings @@ -116,6 +119,8 @@ "GetSubscriptionRequest", "GetTopicRequest", "IngestionDataSourceSettings", + "IngestionFailureEvent", + "JavaScriptUDF", "ListSchemaRevisionsRequest", "ListSchemaRevisionsResponse", "ListSchemasRequest", @@ -131,6 +136,7 @@ "ListTopicsRequest", "ListTopicsResponse", "MessageStoragePolicy", + "MessageTransform", "ModifyAckDeadlineRequest", "ModifyPushConfigRequest", "PlatformLogsSettings", diff --git a/google/pubsub_v1/services/publisher/async_client.py b/google/pubsub_v1/services/publisher/async_client.py index eda259ad5..4fd755d91 100644 --- a/google/pubsub_v1/services/publisher/async_client.py +++ b/google/pubsub_v1/services/publisher/async_client.py @@ -1366,11 +1366,7 @@ async def set_iam_policy( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.set_iam_policy, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self.transport._wrapped_methods[self._client._transport.set_iam_policy] # Certain fields should be provided within the metadata header; # add these here. @@ -1491,11 +1487,7 @@ async def get_iam_policy( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.get_iam_policy, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self.transport._wrapped_methods[self._client._transport.get_iam_policy] # Certain fields should be provided within the metadata header; # add these here. @@ -1554,11 +1546,9 @@ async def test_iam_permissions( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.test_iam_permissions, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self.transport._wrapped_methods[ + self._client._transport.test_iam_permissions + ] # Certain fields should be provided within the metadata header; # add these here. diff --git a/google/pubsub_v1/services/publisher/client.py b/google/pubsub_v1/services/publisher/client.py index 5a4b5a6ff..698aed49e 100644 --- a/google/pubsub_v1/services/publisher/client.py +++ b/google/pubsub_v1/services/publisher/client.py @@ -511,36 +511,6 @@ def _get_universe_domain( raise ValueError("Universe Domain cannot be an empty string.") return universe_domain - @staticmethod - def _compare_universes( - client_universe: str, credentials: ga_credentials.Credentials - ) -> bool: - """Returns True iff the universe domains used by the client and credentials match. - - Args: - client_universe (str): The universe domain configured via the client options. - credentials (ga_credentials.Credentials): The credentials being used in the client. - - Returns: - bool: True iff client_universe matches the universe in credentials. - - Raises: - ValueError: when client_universe does not match the universe in credentials. - """ - - default_universe = PublisherClient._DEFAULT_UNIVERSE - credentials_universe = getattr(credentials, "universe_domain", default_universe) - - if client_universe != credentials_universe: - raise ValueError( - "The configured universe domain " - f"({client_universe}) does not match the universe domain " - f"found in the credentials ({credentials_universe}). " - "If you haven't configured the universe domain explicitly, " - f"`{default_universe}` is the default." - ) - return True - def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. @@ -550,13 +520,9 @@ def _validate_universe_domain(self): Raises: ValueError: If the configured universe domain is not valid. """ - self._is_universe_domain_valid = ( - self._is_universe_domain_valid - or PublisherClient._compare_universes( - self.universe_domain, self.transport._credentials - ) - ) - return self._is_universe_domain_valid + + # NOTE (b/349488459): universe validation is disabled until further notice. + return True @property def api_endpoint(self): diff --git a/google/pubsub_v1/services/publisher/transports/README.rst b/google/pubsub_v1/services/publisher/transports/README.rst new file mode 100644 index 000000000..489748f4d --- /dev/null +++ b/google/pubsub_v1/services/publisher/transports/README.rst @@ -0,0 +1,9 @@ + +transport inheritance structure +_______________________________ + +`PublisherTransport` is the ABC for all transports. +- public child `PublisherGrpcTransport` for sync gRPC transport (defined in `grpc.py`). +- public child `PublisherGrpcAsyncIOTransport` for async gRPC transport (defined in `grpc_asyncio.py`). +- private child `_BasePublisherRestTransport` for base REST transport with inner classes `_BaseMETHOD` (defined in `rest_base.py`). +- public child `PublisherRestTransport` for sync REST transport with inner classes `METHOD` derived from the parent's corresponding `_BaseMETHOD` classes (defined in `rest.py`). diff --git a/google/pubsub_v1/services/publisher/transports/base.py b/google/pubsub_v1/services/publisher/transports/base.py index 800ba82ce..45b06302d 100644 --- a/google/pubsub_v1/services/publisher/transports/base.py +++ b/google/pubsub_v1/services/publisher/transports/base.py @@ -275,6 +275,21 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), + self.get_iam_policy: gapic_v1.method.wrap_method( + self.get_iam_policy, + default_timeout=None, + client_info=client_info, + ), + self.set_iam_policy: gapic_v1.method.wrap_method( + self.set_iam_policy, + default_timeout=None, + client_info=client_info, + ), + self.test_iam_permissions: gapic_v1.method.wrap_method( + self.test_iam_permissions, + default_timeout=None, + client_info=client_info, + ), } def close(self): diff --git a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py index 3d98d6b51..6a293137a 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import inspect import warnings from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union @@ -233,6 +234,9 @@ def __init__( ) # Wrap messages. This must be done after self._grpc_channel exists + self._wrap_with_kind = ( + "kind" in inspect.signature(gapic_v1.method_async.wrap_method).parameters + ) self._prep_wrapped_messages(client_info) @property @@ -585,7 +589,7 @@ def test_iam_permissions( def _prep_wrapped_messages(self, client_info): """Precompute the wrapped methods, overriding the base class method to use async wrappers.""" self._wrapped_methods = { - self.create_topic: gapic_v1.method_async.wrap_method( + self.create_topic: self._wrap_method( self.create_topic, default_retry=retries.AsyncRetry( initial=0.1, @@ -599,7 +603,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.update_topic: gapic_v1.method_async.wrap_method( + self.update_topic: self._wrap_method( self.update_topic, default_retry=retries.AsyncRetry( initial=0.1, @@ -613,7 +617,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.publish: gapic_v1.method_async.wrap_method( + self.publish: self._wrap_method( self.publish, default_retry=retries.AsyncRetry( initial=0.1, @@ -633,7 +637,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.get_topic: gapic_v1.method_async.wrap_method( + self.get_topic: self._wrap_method( self.get_topic, default_retry=retries.AsyncRetry( initial=0.1, @@ -649,7 +653,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.list_topics: gapic_v1.method_async.wrap_method( + self.list_topics: self._wrap_method( self.list_topics, default_retry=retries.AsyncRetry( initial=0.1, @@ -665,7 +669,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.list_topic_subscriptions: gapic_v1.method_async.wrap_method( + self.list_topic_subscriptions: self._wrap_method( self.list_topic_subscriptions, default_retry=retries.AsyncRetry( initial=0.1, @@ -681,7 +685,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.list_topic_snapshots: gapic_v1.method_async.wrap_method( + self.list_topic_snapshots: self._wrap_method( self.list_topic_snapshots, default_retry=retries.AsyncRetry( initial=0.1, @@ -697,7 +701,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.delete_topic: gapic_v1.method_async.wrap_method( + self.delete_topic: self._wrap_method( self.delete_topic, default_retry=retries.AsyncRetry( initial=0.1, @@ -711,7 +715,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.detach_subscription: gapic_v1.method_async.wrap_method( + self.detach_subscription: self._wrap_method( self.detach_subscription, default_retry=retries.AsyncRetry( initial=0.1, @@ -725,10 +729,34 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), + self.get_iam_policy: self._wrap_method( + self.get_iam_policy, + default_timeout=None, + client_info=client_info, + ), + self.set_iam_policy: self._wrap_method( + self.set_iam_policy, + default_timeout=None, + client_info=client_info, + ), + self.test_iam_permissions: self._wrap_method( + self.test_iam_permissions, + default_timeout=None, + client_info=client_info, + ), } + def _wrap_method(self, func, *args, **kwargs): + if self._wrap_with_kind: # pragma: NO COVER + kwargs["kind"] = self.kind + return gapic_v1.method_async.wrap_method(func, *args, **kwargs) + def close(self): return self.grpc_channel.close() + @property + def kind(self) -> str: + return "grpc_asyncio" + __all__ = ("PublisherGrpcAsyncIOTransport",) diff --git a/google/pubsub_v1/services/publisher/transports/rest.py b/google/pubsub_v1/services/publisher/transports/rest.py index 4d70c3e64..3685dd55f 100644 --- a/google/pubsub_v1/services/publisher/transports/rest.py +++ b/google/pubsub_v1/services/publisher/transports/rest.py @@ -16,43 +16,42 @@ from google.auth.transport.requests import AuthorizedSession # type: ignore import json # type: ignore -import grpc # type: ignore -from google.auth.transport.grpc import SslCredentials # type: ignore from google.auth import credentials as ga_credentials # type: ignore from google.api_core import exceptions as core_exceptions from google.api_core import retry as retries from google.api_core import rest_helpers from google.api_core import rest_streaming -from google.api_core import path_template from google.api_core import gapic_v1 from google.protobuf import json_format from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore + from requests import __version__ as requests_version import dataclasses -import re from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union import warnings -try: - OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] -except AttributeError: # pragma: NO COVER - OptionalRetry = Union[retries.Retry, object, None] # type: ignore - from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore from google.protobuf import empty_pb2 # type: ignore from google.pubsub_v1.types import pubsub -from .base import PublisherTransport, DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO + +from .rest_base import _BasePublisherRestTransport +from .base import DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO + +try: + OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] +except AttributeError: # pragma: NO COVER + OptionalRetry = Union[retries.Retry, object, None] # type: ignore DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version, grpc_version=None, - rest_version=requests_version, + rest_version=f"requests@{requests_version}", ) @@ -394,8 +393,8 @@ class PublisherRestStub: _interceptor: PublisherRestInterceptor -class PublisherRestTransport(PublisherTransport): - """REST backend transport for Publisher. +class PublisherRestTransport(_BasePublisherRestTransport): + """REST backend synchronous transport for Publisher. The service that an application uses to manipulate topics, and to send messages to a topic. @@ -405,7 +404,6 @@ class PublisherRestTransport(PublisherTransport): and call it. It sends JSON representations of protocol buffers over HTTP/1.1 - """ def __init__( @@ -459,21 +457,12 @@ def __init__( # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the # credentials object - maybe_url_match = re.match("^(?Phttp(?:s)?://)?(?P.*)$", host) - if maybe_url_match is None: - raise ValueError( - f"Unexpected hostname structure: {host}" - ) # pragma: NO COVER - - url_match_items = maybe_url_match.groupdict() - - host = f"{url_scheme}://{host}" if not url_match_items["scheme"] else host - super().__init__( host=host, credentials=credentials, client_info=client_info, always_use_jwt_access=always_use_jwt_access, + url_scheme=url_scheme, api_audience=api_audience, ) self._session = AuthorizedSession( @@ -484,19 +473,32 @@ def __init__( self._interceptor = interceptor or PublisherRestInterceptor() self._prep_wrapped_messages(client_info) - class _CreateTopic(PublisherRestStub): + class _CreateTopic(_BasePublisherRestTransport._BaseCreateTopic, PublisherRestStub): def __hash__(self): - return hash("CreateTopic") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("PublisherRestTransport.CreateTopic") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -522,45 +524,36 @@ def __call__( A topic resource. """ - http_options: List[Dict[str, str]] = [ - { - "method": "put", - "uri": "/v1/{name=projects/*/topics/*}", - "body": "*", - }, - ] + http_options = ( + _BasePublisherRestTransport._BaseCreateTopic._get_http_options() + ) request, metadata = self._interceptor.pre_create_topic(request, metadata) - pb_request = pubsub.Topic.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = ( + _BasePublisherRestTransport._BaseCreateTopic._get_transcoded_request( + http_options, request + ) + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = _BasePublisherRestTransport._BaseCreateTopic._get_request_body_json( + transcoded_request ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BasePublisherRestTransport._BaseCreateTopic._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = PublisherRestTransport._CreateTopic._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -576,19 +569,31 @@ def __call__( resp = self._interceptor.post_create_topic(resp) return resp - class _DeleteTopic(PublisherRestStub): + class _DeleteTopic(_BasePublisherRestTransport._BaseDeleteTopic, PublisherRestStub): def __hash__(self): - return hash("DeleteTopic") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("PublisherRestTransport.DeleteTopic") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -610,38 +615,31 @@ def __call__( sent along with the request as metadata. """ - http_options: List[Dict[str, str]] = [ - { - "method": "delete", - "uri": "/v1/{topic=projects/*/topics/*}", - }, - ] + http_options = ( + _BasePublisherRestTransport._BaseDeleteTopic._get_http_options() + ) request, metadata = self._interceptor.pre_delete_topic(request, metadata) - pb_request = pubsub.DeleteTopicRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = ( + _BasePublisherRestTransport._BaseDeleteTopic._get_transcoded_request( + http_options, request + ) + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BasePublisherRestTransport._BaseDeleteTopic._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = PublisherRestTransport._DeleteTopic._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -649,19 +647,33 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) - class _DetachSubscription(PublisherRestStub): + class _DetachSubscription( + _BasePublisherRestTransport._BaseDetachSubscription, PublisherRestStub + ): def __hash__(self): - return hash("DetachSubscription") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("PublisherRestTransport.DetachSubscription") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -690,40 +702,29 @@ def __call__( """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{subscription=projects/*/subscriptions/*}:detach", - }, - ] + http_options = ( + _BasePublisherRestTransport._BaseDetachSubscription._get_http_options() + ) request, metadata = self._interceptor.pre_detach_subscription( request, metadata ) - pb_request = pubsub.DetachSubscriptionRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = _BasePublisherRestTransport._BaseDetachSubscription._get_transcoded_request( + http_options, request + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BasePublisherRestTransport._BaseDetachSubscription._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = PublisherRestTransport._DetachSubscription._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -739,19 +740,31 @@ def __call__( resp = self._interceptor.post_detach_subscription(resp) return resp - class _GetTopic(PublisherRestStub): + class _GetTopic(_BasePublisherRestTransport._BaseGetTopic, PublisherRestStub): def __hash__(self): - return hash("GetTopic") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("PublisherRestTransport.GetTopic") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -777,38 +790,29 @@ def __call__( A topic resource. """ - http_options: List[Dict[str, str]] = [ - { - "method": "get", - "uri": "/v1/{topic=projects/*/topics/*}", - }, - ] + http_options = _BasePublisherRestTransport._BaseGetTopic._get_http_options() request, metadata = self._interceptor.pre_get_topic(request, metadata) - pb_request = pubsub.GetTopicRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = ( + _BasePublisherRestTransport._BaseGetTopic._get_transcoded_request( + http_options, request + ) + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BasePublisherRestTransport._BaseGetTopic._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = PublisherRestTransport._GetTopic._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -824,19 +828,31 @@ def __call__( resp = self._interceptor.post_get_topic(resp) return resp - class _ListTopics(PublisherRestStub): + class _ListTopics(_BasePublisherRestTransport._BaseListTopics, PublisherRestStub): def __hash__(self): - return hash("ListTopics") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("PublisherRestTransport.ListTopics") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -862,38 +878,31 @@ def __call__( Response for the ``ListTopics`` method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "get", - "uri": "/v1/{project=projects/*}/topics", - }, - ] + http_options = ( + _BasePublisherRestTransport._BaseListTopics._get_http_options() + ) request, metadata = self._interceptor.pre_list_topics(request, metadata) - pb_request = pubsub.ListTopicsRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = ( + _BasePublisherRestTransport._BaseListTopics._get_transcoded_request( + http_options, request + ) + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BasePublisherRestTransport._BaseListTopics._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = PublisherRestTransport._ListTopics._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -909,19 +918,33 @@ def __call__( resp = self._interceptor.post_list_topics(resp) return resp - class _ListTopicSnapshots(PublisherRestStub): + class _ListTopicSnapshots( + _BasePublisherRestTransport._BaseListTopicSnapshots, PublisherRestStub + ): def __hash__(self): - return hash("ListTopicSnapshots") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("PublisherRestTransport.ListTopicSnapshots") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -947,40 +970,29 @@ def __call__( Response for the ``ListTopicSnapshots`` method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "get", - "uri": "/v1/{topic=projects/*/topics/*}/snapshots", - }, - ] + http_options = ( + _BasePublisherRestTransport._BaseListTopicSnapshots._get_http_options() + ) request, metadata = self._interceptor.pre_list_topic_snapshots( request, metadata ) - pb_request = pubsub.ListTopicSnapshotsRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = _BasePublisherRestTransport._BaseListTopicSnapshots._get_transcoded_request( + http_options, request + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BasePublisherRestTransport._BaseListTopicSnapshots._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = PublisherRestTransport._ListTopicSnapshots._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -996,19 +1008,33 @@ def __call__( resp = self._interceptor.post_list_topic_snapshots(resp) return resp - class _ListTopicSubscriptions(PublisherRestStub): + class _ListTopicSubscriptions( + _BasePublisherRestTransport._BaseListTopicSubscriptions, PublisherRestStub + ): def __hash__(self): - return hash("ListTopicSubscriptions") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("PublisherRestTransport.ListTopicSubscriptions") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -1034,40 +1060,29 @@ def __call__( Response for the ``ListTopicSubscriptions`` method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "get", - "uri": "/v1/{topic=projects/*/topics/*}/subscriptions", - }, - ] + http_options = ( + _BasePublisherRestTransport._BaseListTopicSubscriptions._get_http_options() + ) request, metadata = self._interceptor.pre_list_topic_subscriptions( request, metadata ) - pb_request = pubsub.ListTopicSubscriptionsRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = _BasePublisherRestTransport._BaseListTopicSubscriptions._get_transcoded_request( + http_options, request + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BasePublisherRestTransport._BaseListTopicSubscriptions._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = PublisherRestTransport._ListTopicSubscriptions._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1083,19 +1098,32 @@ def __call__( resp = self._interceptor.post_list_topic_subscriptions(resp) return resp - class _Publish(PublisherRestStub): + class _Publish(_BasePublisherRestTransport._BasePublish, PublisherRestStub): def __hash__(self): - return hash("Publish") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("PublisherRestTransport.Publish") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -1121,45 +1149,34 @@ def __call__( Response for the ``Publish`` method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{topic=projects/*/topics/*}:publish", - "body": "*", - }, - ] + http_options = _BasePublisherRestTransport._BasePublish._get_http_options() request, metadata = self._interceptor.pre_publish(request, metadata) - pb_request = pubsub.PublishRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = ( + _BasePublisherRestTransport._BasePublish._get_transcoded_request( + http_options, request + ) + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = _BasePublisherRestTransport._BasePublish._get_request_body_json( + transcoded_request ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BasePublisherRestTransport._BasePublish._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = PublisherRestTransport._Publish._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1175,19 +1192,32 @@ def __call__( resp = self._interceptor.post_publish(resp) return resp - class _UpdateTopic(PublisherRestStub): + class _UpdateTopic(_BasePublisherRestTransport._BaseUpdateTopic, PublisherRestStub): def __hash__(self): - return hash("UpdateTopic") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("PublisherRestTransport.UpdateTopic") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -1213,45 +1243,36 @@ def __call__( A topic resource. """ - http_options: List[Dict[str, str]] = [ - { - "method": "patch", - "uri": "/v1/{topic.name=projects/*/topics/*}", - "body": "*", - }, - ] + http_options = ( + _BasePublisherRestTransport._BaseUpdateTopic._get_http_options() + ) request, metadata = self._interceptor.pre_update_topic(request, metadata) - pb_request = pubsub.UpdateTopicRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = ( + _BasePublisherRestTransport._BaseUpdateTopic._get_transcoded_request( + http_options, request + ) + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = _BasePublisherRestTransport._BaseUpdateTopic._get_request_body_json( + transcoded_request ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BasePublisherRestTransport._BaseUpdateTopic._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = PublisherRestTransport._UpdateTopic._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1339,7 +1360,34 @@ def update_topic(self) -> Callable[[pubsub.UpdateTopicRequest], pubsub.Topic]: def get_iam_policy(self): return self._GetIamPolicy(self._session, self._host, self._interceptor) # type: ignore - class _GetIamPolicy(PublisherRestStub): + class _GetIamPolicy( + _BasePublisherRestTransport._BaseGetIamPolicy, PublisherRestStub + ): + def __hash__(self): + return hash("PublisherRestTransport.GetIamPolicy") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response + def __call__( self, request: iam_policy_pb2.GetIamPolicyRequest, @@ -1363,44 +1411,31 @@ def __call__( policy_pb2.Policy: Response from GetIamPolicy method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "get", - "uri": "/v1/{resource=projects/*/topics/*}:getIamPolicy", - }, - { - "method": "get", - "uri": "/v1/{resource=projects/*/subscriptions/*}:getIamPolicy", - }, - { - "method": "get", - "uri": "/v1/{resource=projects/*/snapshots/*}:getIamPolicy", - }, - { - "method": "get", - "uri": "/v1/{resource=projects/*/schemas/*}:getIamPolicy", - }, - ] - + http_options = ( + _BasePublisherRestTransport._BaseGetIamPolicy._get_http_options() + ) request, metadata = self._interceptor.pre_get_iam_policy(request, metadata) - request_kwargs = json_format.MessageToDict(request) - transcoded_request = path_template.transcode(http_options, **request_kwargs) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = ( + _BasePublisherRestTransport._BaseGetIamPolicy._get_transcoded_request( + http_options, request + ) + ) # Jsonify the query params - query_params = json.loads(json.dumps(transcoded_request["query_params"])) + query_params = ( + _BasePublisherRestTransport._BaseGetIamPolicy._get_query_params_json( + transcoded_request + ) + ) # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params), + response = PublisherRestTransport._GetIamPolicy._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1408,8 +1443,9 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) + content = response.content.decode("utf-8") resp = policy_pb2.Policy() - resp = json_format.Parse(response.content.decode("utf-8"), resp) + resp = json_format.Parse(content, resp) resp = self._interceptor.post_get_iam_policy(resp) return resp @@ -1417,7 +1453,35 @@ def __call__( def set_iam_policy(self): return self._SetIamPolicy(self._session, self._host, self._interceptor) # type: ignore - class _SetIamPolicy(PublisherRestStub): + class _SetIamPolicy( + _BasePublisherRestTransport._BaseSetIamPolicy, PublisherRestStub + ): + def __hash__(self): + return hash("PublisherRestTransport.SetIamPolicy") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response + def __call__( self, request: iam_policy_pb2.SetIamPolicyRequest, @@ -1441,50 +1505,36 @@ def __call__( policy_pb2.Policy: Response from SetIamPolicy method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{resource=projects/*/topics/*}:setIamPolicy", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/subscriptions/*}:setIamPolicy", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/snapshots/*}:setIamPolicy", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/schemas/*}:setIamPolicy", - "body": "*", - }, - ] - + http_options = ( + _BasePublisherRestTransport._BaseSetIamPolicy._get_http_options() + ) request, metadata = self._interceptor.pre_set_iam_policy(request, metadata) - request_kwargs = json_format.MessageToDict(request) - transcoded_request = path_template.transcode(http_options, **request_kwargs) + transcoded_request = ( + _BasePublisherRestTransport._BaseSetIamPolicy._get_transcoded_request( + http_options, request + ) + ) - body = json.dumps(transcoded_request["body"]) - uri = transcoded_request["uri"] - method = transcoded_request["method"] + body = _BasePublisherRestTransport._BaseSetIamPolicy._get_request_body_json( + transcoded_request + ) # Jsonify the query params - query_params = json.loads(json.dumps(transcoded_request["query_params"])) + query_params = ( + _BasePublisherRestTransport._BaseSetIamPolicy._get_query_params_json( + transcoded_request + ) + ) # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params), - data=body, + response = PublisherRestTransport._SetIamPolicy._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1492,8 +1542,9 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) + content = response.content.decode("utf-8") resp = policy_pb2.Policy() - resp = json_format.Parse(response.content.decode("utf-8"), resp) + resp = json_format.Parse(content, resp) resp = self._interceptor.post_set_iam_policy(resp) return resp @@ -1501,7 +1552,35 @@ def __call__( def test_iam_permissions(self): return self._TestIamPermissions(self._session, self._host, self._interceptor) # type: ignore - class _TestIamPermissions(PublisherRestStub): + class _TestIamPermissions( + _BasePublisherRestTransport._BaseTestIamPermissions, PublisherRestStub + ): + def __hash__(self): + return hash("PublisherRestTransport.TestIamPermissions") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response + def __call__( self, request: iam_policy_pb2.TestIamPermissionsRequest, @@ -1525,52 +1604,34 @@ def __call__( iam_policy_pb2.TestIamPermissionsResponse: Response from TestIamPermissions method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{resource=projects/*/subscriptions/*}:testIamPermissions", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/topics/*}:testIamPermissions", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/snapshots/*}:testIamPermissions", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/schemas/*}:testIamPermissions", - "body": "*", - }, - ] - + http_options = ( + _BasePublisherRestTransport._BaseTestIamPermissions._get_http_options() + ) request, metadata = self._interceptor.pre_test_iam_permissions( request, metadata ) - request_kwargs = json_format.MessageToDict(request) - transcoded_request = path_template.transcode(http_options, **request_kwargs) + transcoded_request = _BasePublisherRestTransport._BaseTestIamPermissions._get_transcoded_request( + http_options, request + ) - body = json.dumps(transcoded_request["body"]) - uri = transcoded_request["uri"] - method = transcoded_request["method"] + body = _BasePublisherRestTransport._BaseTestIamPermissions._get_request_body_json( + transcoded_request + ) # Jsonify the query params - query_params = json.loads(json.dumps(transcoded_request["query_params"])) + query_params = _BasePublisherRestTransport._BaseTestIamPermissions._get_query_params_json( + transcoded_request + ) # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params), - data=body, + response = PublisherRestTransport._TestIamPermissions._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1578,8 +1639,9 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) + content = response.content.decode("utf-8") resp = iam_policy_pb2.TestIamPermissionsResponse() - resp = json_format.Parse(response.content.decode("utf-8"), resp) + resp = json_format.Parse(content, resp) resp = self._interceptor.post_test_iam_permissions(resp) return resp diff --git a/google/pubsub_v1/services/publisher/transports/rest_base.py b/google/pubsub_v1/services/publisher/transports/rest_base.py new file mode 100644 index 000000000..1fa78cdd9 --- /dev/null +++ b/google/pubsub_v1/services/publisher/transports/rest_base.py @@ -0,0 +1,680 @@ +# -*- coding: utf-8 -*- +# 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. +# 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 json # type: ignore +from google.api_core import path_template +from google.api_core import gapic_v1 + +from google.protobuf import json_format +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from .base import PublisherTransport, DEFAULT_CLIENT_INFO + +import re +from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union + + +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.protobuf import empty_pb2 # type: ignore +from google.pubsub_v1.types import pubsub + + +class _BasePublisherRestTransport(PublisherTransport): + """Base REST backend transport for Publisher. + + Note: This class is not meant to be used directly. Use its sync and + async sub-classes instead. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends JSON representations of protocol buffers over HTTP/1.1 + """ + + def __init__( + self, + *, + host: str = "pubsub.googleapis.com", + credentials: Optional[Any] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + url_scheme: str = "https", + api_audience: Optional[str] = None, + ) -> None: + """Instantiate the transport. + Args: + host (Optional[str]): + The hostname to connect to (default: 'pubsub.googleapis.com'). + credentials (Optional[Any]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you are developing + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + url_scheme: the protocol scheme for the API endpoint. Normally + "https", but for testing or local servers, + "http" can be specified. + """ + # Run the base constructor + maybe_url_match = re.match("^(?Phttp(?:s)?://)?(?P.*)$", host) + if maybe_url_match is None: + raise ValueError( + f"Unexpected hostname structure: {host}" + ) # pragma: NO COVER + + url_match_items = maybe_url_match.groupdict() + + host = f"{url_scheme}://{host}" if not url_match_items["scheme"] else host + + super().__init__( + host=host, + credentials=credentials, + client_info=client_info, + always_use_jwt_access=always_use_jwt_access, + api_audience=api_audience, + ) + + class _BaseCreateTopic: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "put", + "uri": "/v1/{name=projects/*/topics/*}", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.Topic.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BasePublisherRestTransport._BaseCreateTopic._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseDeleteTopic: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "delete", + "uri": "/v1/{topic=projects/*/topics/*}", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.DeleteTopicRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BasePublisherRestTransport._BaseDeleteTopic._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseDetachSubscription: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{subscription=projects/*/subscriptions/*}:detach", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.DetachSubscriptionRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BasePublisherRestTransport._BaseDetachSubscription._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseGetTopic: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{topic=projects/*/topics/*}", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.GetTopicRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BasePublisherRestTransport._BaseGetTopic._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseListTopics: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{project=projects/*}/topics", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.ListTopicsRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BasePublisherRestTransport._BaseListTopics._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseListTopicSnapshots: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{topic=projects/*/topics/*}/snapshots", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.ListTopicSnapshotsRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BasePublisherRestTransport._BaseListTopicSnapshots._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseListTopicSubscriptions: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{topic=projects/*/topics/*}/subscriptions", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.ListTopicSubscriptionsRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BasePublisherRestTransport._BaseListTopicSubscriptions._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BasePublish: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{topic=projects/*/topics/*}:publish", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.PublishRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BasePublisherRestTransport._BasePublish._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseUpdateTopic: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "patch", + "uri": "/v1/{topic.name=projects/*/topics/*}", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.UpdateTopicRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BasePublisherRestTransport._BaseUpdateTopic._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseGetIamPolicy: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{resource=projects/*/topics/*}:getIamPolicy", + }, + { + "method": "get", + "uri": "/v1/{resource=projects/*/subscriptions/*}:getIamPolicy", + }, + { + "method": "get", + "uri": "/v1/{resource=projects/*/snapshots/*}:getIamPolicy", + }, + { + "method": "get", + "uri": "/v1/{resource=projects/*/schemas/*}:getIamPolicy", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + request_kwargs = json_format.MessageToDict(request) + transcoded_request = path_template.transcode(http_options, **request_kwargs) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads(json.dumps(transcoded_request["query_params"])) + return query_params + + class _BaseSetIamPolicy: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{resource=projects/*/topics/*}:setIamPolicy", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/subscriptions/*}:setIamPolicy", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/snapshots/*}:setIamPolicy", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/schemas/*}:setIamPolicy", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + request_kwargs = json_format.MessageToDict(request) + transcoded_request = path_template.transcode(http_options, **request_kwargs) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + body = json.dumps(transcoded_request["body"]) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads(json.dumps(transcoded_request["query_params"])) + return query_params + + class _BaseTestIamPermissions: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{resource=projects/*/subscriptions/*}:testIamPermissions", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/topics/*}:testIamPermissions", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/snapshots/*}:testIamPermissions", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/schemas/*}:testIamPermissions", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + request_kwargs = json_format.MessageToDict(request) + transcoded_request = path_template.transcode(http_options, **request_kwargs) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + body = json.dumps(transcoded_request["body"]) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads(json.dumps(transcoded_request["query_params"])) + return query_params + + +__all__ = ("_BasePublisherRestTransport",) diff --git a/google/pubsub_v1/services/schema_service/async_client.py b/google/pubsub_v1/services/schema_service/async_client.py index 9c8eecaec..2d160b062 100644 --- a/google/pubsub_v1/services/schema_service/async_client.py +++ b/google/pubsub_v1/services/schema_service/async_client.py @@ -1476,11 +1476,7 @@ async def set_iam_policy( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.set_iam_policy, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self.transport._wrapped_methods[self._client._transport.set_iam_policy] # Certain fields should be provided within the metadata header; # add these here. @@ -1600,11 +1596,7 @@ async def get_iam_policy( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.get_iam_policy, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self.transport._wrapped_methods[self._client._transport.get_iam_policy] # Certain fields should be provided within the metadata header; # add these here. @@ -1662,11 +1654,9 @@ async def test_iam_permissions( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.test_iam_permissions, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self.transport._wrapped_methods[ + self._client._transport.test_iam_permissions + ] # Certain fields should be provided within the metadata header; # add these here. diff --git a/google/pubsub_v1/services/schema_service/client.py b/google/pubsub_v1/services/schema_service/client.py index dff44944d..c56f8a4f3 100644 --- a/google/pubsub_v1/services/schema_service/client.py +++ b/google/pubsub_v1/services/schema_service/client.py @@ -460,36 +460,6 @@ def _get_universe_domain( raise ValueError("Universe Domain cannot be an empty string.") return universe_domain - @staticmethod - def _compare_universes( - client_universe: str, credentials: ga_credentials.Credentials - ) -> bool: - """Returns True iff the universe domains used by the client and credentials match. - - Args: - client_universe (str): The universe domain configured via the client options. - credentials (ga_credentials.Credentials): The credentials being used in the client. - - Returns: - bool: True iff client_universe matches the universe in credentials. - - Raises: - ValueError: when client_universe does not match the universe in credentials. - """ - - default_universe = SchemaServiceClient._DEFAULT_UNIVERSE - credentials_universe = getattr(credentials, "universe_domain", default_universe) - - if client_universe != credentials_universe: - raise ValueError( - "The configured universe domain " - f"({client_universe}) does not match the universe domain " - f"found in the credentials ({credentials_universe}). " - "If you haven't configured the universe domain explicitly, " - f"`{default_universe}` is the default." - ) - return True - def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. @@ -499,13 +469,9 @@ def _validate_universe_domain(self): Raises: ValueError: If the configured universe domain is not valid. """ - self._is_universe_domain_valid = ( - self._is_universe_domain_valid - or SchemaServiceClient._compare_universes( - self.universe_domain, self.transport._credentials - ) - ) - return self._is_universe_domain_valid + + # NOTE (b/349488459): universe validation is disabled until further notice. + return True @property def api_endpoint(self): diff --git a/google/pubsub_v1/services/schema_service/transports/README.rst b/google/pubsub_v1/services/schema_service/transports/README.rst new file mode 100644 index 000000000..a0a06949e --- /dev/null +++ b/google/pubsub_v1/services/schema_service/transports/README.rst @@ -0,0 +1,9 @@ + +transport inheritance structure +_______________________________ + +`SchemaServiceTransport` is the ABC for all transports. +- public child `SchemaServiceGrpcTransport` for sync gRPC transport (defined in `grpc.py`). +- public child `SchemaServiceGrpcAsyncIOTransport` for async gRPC transport (defined in `grpc_asyncio.py`). +- private child `_BaseSchemaServiceRestTransport` for base REST transport with inner classes `_BaseMETHOD` (defined in `rest_base.py`). +- public child `SchemaServiceRestTransport` for sync REST transport with inner classes `METHOD` derived from the parent's corresponding `_BaseMETHOD` classes (defined in `rest.py`). diff --git a/google/pubsub_v1/services/schema_service/transports/base.py b/google/pubsub_v1/services/schema_service/transports/base.py index 5c7f35aa8..e42f9896f 100644 --- a/google/pubsub_v1/services/schema_service/transports/base.py +++ b/google/pubsub_v1/services/schema_service/transports/base.py @@ -276,6 +276,21 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), + self.get_iam_policy: gapic_v1.method.wrap_method( + self.get_iam_policy, + default_timeout=None, + client_info=client_info, + ), + self.set_iam_policy: gapic_v1.method.wrap_method( + self.set_iam_policy, + default_timeout=None, + client_info=client_info, + ), + self.test_iam_permissions: gapic_v1.method.wrap_method( + self.test_iam_permissions, + default_timeout=None, + client_info=client_info, + ), } def close(self): diff --git a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py index 71c362436..e642ed1b9 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import inspect import warnings from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union @@ -233,6 +234,9 @@ def __init__( ) # Wrap messages. This must be done after self._grpc_channel exists + self._wrap_with_kind = ( + "kind" in inspect.signature(gapic_v1.method_async.wrap_method).parameters + ) self._prep_wrapped_messages(client_info) @property @@ -596,7 +600,7 @@ def test_iam_permissions( def _prep_wrapped_messages(self, client_info): """Precompute the wrapped methods, overriding the base class method to use async wrappers.""" self._wrapped_methods = { - self.create_schema: gapic_v1.method_async.wrap_method( + self.create_schema: self._wrap_method( self.create_schema, default_retry=retries.AsyncRetry( initial=0.1, @@ -610,7 +614,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.get_schema: gapic_v1.method_async.wrap_method( + self.get_schema: self._wrap_method( self.get_schema, default_retry=retries.AsyncRetry( initial=0.1, @@ -624,7 +628,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.list_schemas: gapic_v1.method_async.wrap_method( + self.list_schemas: self._wrap_method( self.list_schemas, default_retry=retries.AsyncRetry( initial=0.1, @@ -638,7 +642,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.list_schema_revisions: gapic_v1.method_async.wrap_method( + self.list_schema_revisions: self._wrap_method( self.list_schema_revisions, default_retry=retries.AsyncRetry( initial=0.1, @@ -652,7 +656,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.commit_schema: gapic_v1.method_async.wrap_method( + self.commit_schema: self._wrap_method( self.commit_schema, default_retry=retries.AsyncRetry( initial=0.1, @@ -666,7 +670,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.rollback_schema: gapic_v1.method_async.wrap_method( + self.rollback_schema: self._wrap_method( self.rollback_schema, default_retry=retries.AsyncRetry( initial=0.1, @@ -680,7 +684,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.delete_schema_revision: gapic_v1.method_async.wrap_method( + self.delete_schema_revision: self._wrap_method( self.delete_schema_revision, default_retry=retries.AsyncRetry( initial=0.1, @@ -694,7 +698,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.delete_schema: gapic_v1.method_async.wrap_method( + self.delete_schema: self._wrap_method( self.delete_schema, default_retry=retries.AsyncRetry( initial=0.1, @@ -708,7 +712,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.validate_schema: gapic_v1.method_async.wrap_method( + self.validate_schema: self._wrap_method( self.validate_schema, default_retry=retries.AsyncRetry( initial=0.1, @@ -722,7 +726,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.validate_message: gapic_v1.method_async.wrap_method( + self.validate_message: self._wrap_method( self.validate_message, default_retry=retries.AsyncRetry( initial=0.1, @@ -736,10 +740,34 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), + self.get_iam_policy: self._wrap_method( + self.get_iam_policy, + default_timeout=None, + client_info=client_info, + ), + self.set_iam_policy: self._wrap_method( + self.set_iam_policy, + default_timeout=None, + client_info=client_info, + ), + self.test_iam_permissions: self._wrap_method( + self.test_iam_permissions, + default_timeout=None, + client_info=client_info, + ), } + def _wrap_method(self, func, *args, **kwargs): + if self._wrap_with_kind: # pragma: NO COVER + kwargs["kind"] = self.kind + return gapic_v1.method_async.wrap_method(func, *args, **kwargs) + def close(self): return self.grpc_channel.close() + @property + def kind(self) -> str: + return "grpc_asyncio" + __all__ = ("SchemaServiceGrpcAsyncIOTransport",) diff --git a/google/pubsub_v1/services/schema_service/transports/rest.py b/google/pubsub_v1/services/schema_service/transports/rest.py index 953e58052..7cf86c808 100644 --- a/google/pubsub_v1/services/schema_service/transports/rest.py +++ b/google/pubsub_v1/services/schema_service/transports/rest.py @@ -16,30 +16,22 @@ from google.auth.transport.requests import AuthorizedSession # type: ignore import json # type: ignore -import grpc # type: ignore -from google.auth.transport.grpc import SslCredentials # type: ignore from google.auth import credentials as ga_credentials # type: ignore from google.api_core import exceptions as core_exceptions from google.api_core import retry as retries from google.api_core import rest_helpers from google.api_core import rest_streaming -from google.api_core import path_template from google.api_core import gapic_v1 from google.protobuf import json_format from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore + from requests import __version__ as requests_version import dataclasses -import re from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union import warnings -try: - OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] -except AttributeError: # pragma: NO COVER - OptionalRetry = Union[retries.Retry, object, None] # type: ignore - from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -47,16 +39,20 @@ from google.pubsub_v1.types import schema from google.pubsub_v1.types import schema as gp_schema -from .base import ( - SchemaServiceTransport, - DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO, -) + +from .rest_base import _BaseSchemaServiceRestTransport +from .base import DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO + +try: + OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] +except AttributeError: # pragma: NO COVER + OptionalRetry = Union[retries.Retry, object, None] # type: ignore DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version, grpc_version=None, - rest_version=requests_version, + rest_version=f"requests@{requests_version}", ) @@ -431,8 +427,8 @@ class SchemaServiceRestStub: _interceptor: SchemaServiceRestInterceptor -class SchemaServiceRestTransport(SchemaServiceTransport): - """REST backend transport for SchemaService. +class SchemaServiceRestTransport(_BaseSchemaServiceRestTransport): + """REST backend synchronous transport for SchemaService. Service for doing schema-related operations. @@ -441,7 +437,6 @@ class SchemaServiceRestTransport(SchemaServiceTransport): and call it. It sends JSON representations of protocol buffers over HTTP/1.1 - """ def __init__( @@ -495,21 +490,12 @@ def __init__( # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the # credentials object - maybe_url_match = re.match("^(?Phttp(?:s)?://)?(?P.*)$", host) - if maybe_url_match is None: - raise ValueError( - f"Unexpected hostname structure: {host}" - ) # pragma: NO COVER - - url_match_items = maybe_url_match.groupdict() - - host = f"{url_scheme}://{host}" if not url_match_items["scheme"] else host - super().__init__( host=host, credentials=credentials, client_info=client_info, always_use_jwt_access=always_use_jwt_access, + url_scheme=url_scheme, api_audience=api_audience, ) self._session = AuthorizedSession( @@ -520,19 +506,34 @@ def __init__( self._interceptor = interceptor or SchemaServiceRestInterceptor() self._prep_wrapped_messages(client_info) - class _CommitSchema(SchemaServiceRestStub): + class _CommitSchema( + _BaseSchemaServiceRestTransport._BaseCommitSchema, SchemaServiceRestStub + ): def __hash__(self): - return hash("CommitSchema") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SchemaServiceRestTransport.CommitSchema") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -558,45 +559,32 @@ def __call__( A schema resource. """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{name=projects/*/schemas/*}:commit", - "body": "*", - }, - ] + http_options = ( + _BaseSchemaServiceRestTransport._BaseCommitSchema._get_http_options() + ) request, metadata = self._interceptor.pre_commit_schema(request, metadata) - pb_request = gp_schema.CommitSchemaRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = _BaseSchemaServiceRestTransport._BaseCommitSchema._get_transcoded_request( + http_options, request + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = _BaseSchemaServiceRestTransport._BaseCommitSchema._get_request_body_json( + transcoded_request ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BaseSchemaServiceRestTransport._BaseCommitSchema._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = SchemaServiceRestTransport._CommitSchema._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -612,19 +600,34 @@ def __call__( resp = self._interceptor.post_commit_schema(resp) return resp - class _CreateSchema(SchemaServiceRestStub): + class _CreateSchema( + _BaseSchemaServiceRestTransport._BaseCreateSchema, SchemaServiceRestStub + ): def __hash__(self): - return hash("CreateSchema") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SchemaServiceRestTransport.CreateSchema") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -650,45 +653,32 @@ def __call__( A schema resource. """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{parent=projects/*}/schemas", - "body": "schema", - }, - ] + http_options = ( + _BaseSchemaServiceRestTransport._BaseCreateSchema._get_http_options() + ) request, metadata = self._interceptor.pre_create_schema(request, metadata) - pb_request = gp_schema.CreateSchemaRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = _BaseSchemaServiceRestTransport._BaseCreateSchema._get_transcoded_request( + http_options, request + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = _BaseSchemaServiceRestTransport._BaseCreateSchema._get_request_body_json( + transcoded_request ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BaseSchemaServiceRestTransport._BaseCreateSchema._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = SchemaServiceRestTransport._CreateSchema._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -704,19 +694,33 @@ def __call__( resp = self._interceptor.post_create_schema(resp) return resp - class _DeleteSchema(SchemaServiceRestStub): + class _DeleteSchema( + _BaseSchemaServiceRestTransport._BaseDeleteSchema, SchemaServiceRestStub + ): def __hash__(self): - return hash("DeleteSchema") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SchemaServiceRestTransport.DeleteSchema") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -738,38 +742,27 @@ def __call__( sent along with the request as metadata. """ - http_options: List[Dict[str, str]] = [ - { - "method": "delete", - "uri": "/v1/{name=projects/*/schemas/*}", - }, - ] + http_options = ( + _BaseSchemaServiceRestTransport._BaseDeleteSchema._get_http_options() + ) request, metadata = self._interceptor.pre_delete_schema(request, metadata) - pb_request = schema.DeleteSchemaRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = _BaseSchemaServiceRestTransport._BaseDeleteSchema._get_transcoded_request( + http_options, request + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BaseSchemaServiceRestTransport._BaseDeleteSchema._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = SchemaServiceRestTransport._DeleteSchema._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -777,19 +770,33 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) - class _DeleteSchemaRevision(SchemaServiceRestStub): + class _DeleteSchemaRevision( + _BaseSchemaServiceRestTransport._BaseDeleteSchemaRevision, SchemaServiceRestStub + ): def __hash__(self): - return hash("DeleteSchemaRevision") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SchemaServiceRestTransport.DeleteSchemaRevision") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -815,40 +822,29 @@ def __call__( A schema resource. """ - http_options: List[Dict[str, str]] = [ - { - "method": "delete", - "uri": "/v1/{name=projects/*/schemas/*}:deleteRevision", - }, - ] + http_options = ( + _BaseSchemaServiceRestTransport._BaseDeleteSchemaRevision._get_http_options() + ) request, metadata = self._interceptor.pre_delete_schema_revision( request, metadata ) - pb_request = schema.DeleteSchemaRevisionRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = _BaseSchemaServiceRestTransport._BaseDeleteSchemaRevision._get_transcoded_request( + http_options, request + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BaseSchemaServiceRestTransport._BaseDeleteSchemaRevision._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = SchemaServiceRestTransport._DeleteSchemaRevision._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -864,19 +860,33 @@ def __call__( resp = self._interceptor.post_delete_schema_revision(resp) return resp - class _GetSchema(SchemaServiceRestStub): + class _GetSchema( + _BaseSchemaServiceRestTransport._BaseGetSchema, SchemaServiceRestStub + ): def __hash__(self): - return hash("GetSchema") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SchemaServiceRestTransport.GetSchema") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -902,38 +912,31 @@ def __call__( A schema resource. """ - http_options: List[Dict[str, str]] = [ - { - "method": "get", - "uri": "/v1/{name=projects/*/schemas/*}", - }, - ] + http_options = ( + _BaseSchemaServiceRestTransport._BaseGetSchema._get_http_options() + ) request, metadata = self._interceptor.pre_get_schema(request, metadata) - pb_request = schema.GetSchemaRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = ( + _BaseSchemaServiceRestTransport._BaseGetSchema._get_transcoded_request( + http_options, request + ) + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BaseSchemaServiceRestTransport._BaseGetSchema._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = SchemaServiceRestTransport._GetSchema._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -949,19 +952,33 @@ def __call__( resp = self._interceptor.post_get_schema(resp) return resp - class _ListSchemaRevisions(SchemaServiceRestStub): + class _ListSchemaRevisions( + _BaseSchemaServiceRestTransport._BaseListSchemaRevisions, SchemaServiceRestStub + ): def __hash__(self): - return hash("ListSchemaRevisions") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SchemaServiceRestTransport.ListSchemaRevisions") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -987,40 +1004,29 @@ def __call__( Response for the ``ListSchemaRevisions`` method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "get", - "uri": "/v1/{name=projects/*/schemas/*}:listRevisions", - }, - ] + http_options = ( + _BaseSchemaServiceRestTransport._BaseListSchemaRevisions._get_http_options() + ) request, metadata = self._interceptor.pre_list_schema_revisions( request, metadata ) - pb_request = schema.ListSchemaRevisionsRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = _BaseSchemaServiceRestTransport._BaseListSchemaRevisions._get_transcoded_request( + http_options, request + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BaseSchemaServiceRestTransport._BaseListSchemaRevisions._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = SchemaServiceRestTransport._ListSchemaRevisions._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1036,19 +1042,33 @@ def __call__( resp = self._interceptor.post_list_schema_revisions(resp) return resp - class _ListSchemas(SchemaServiceRestStub): + class _ListSchemas( + _BaseSchemaServiceRestTransport._BaseListSchemas, SchemaServiceRestStub + ): def __hash__(self): - return hash("ListSchemas") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SchemaServiceRestTransport.ListSchemas") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -1074,38 +1094,29 @@ def __call__( Response for the ``ListSchemas`` method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "get", - "uri": "/v1/{parent=projects/*}/schemas", - }, - ] + http_options = ( + _BaseSchemaServiceRestTransport._BaseListSchemas._get_http_options() + ) request, metadata = self._interceptor.pre_list_schemas(request, metadata) - pb_request = schema.ListSchemasRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = _BaseSchemaServiceRestTransport._BaseListSchemas._get_transcoded_request( + http_options, request + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BaseSchemaServiceRestTransport._BaseListSchemas._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = SchemaServiceRestTransport._ListSchemas._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1121,19 +1132,34 @@ def __call__( resp = self._interceptor.post_list_schemas(resp) return resp - class _RollbackSchema(SchemaServiceRestStub): + class _RollbackSchema( + _BaseSchemaServiceRestTransport._BaseRollbackSchema, SchemaServiceRestStub + ): def __hash__(self): - return hash("RollbackSchema") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SchemaServiceRestTransport.RollbackSchema") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -1159,45 +1185,32 @@ def __call__( A schema resource. """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{name=projects/*/schemas/*}:rollback", - "body": "*", - }, - ] + http_options = ( + _BaseSchemaServiceRestTransport._BaseRollbackSchema._get_http_options() + ) request, metadata = self._interceptor.pre_rollback_schema(request, metadata) - pb_request = schema.RollbackSchemaRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = _BaseSchemaServiceRestTransport._BaseRollbackSchema._get_transcoded_request( + http_options, request + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = _BaseSchemaServiceRestTransport._BaseRollbackSchema._get_request_body_json( + transcoded_request ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BaseSchemaServiceRestTransport._BaseRollbackSchema._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = SchemaServiceRestTransport._RollbackSchema._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1213,19 +1226,34 @@ def __call__( resp = self._interceptor.post_rollback_schema(resp) return resp - class _ValidateMessage(SchemaServiceRestStub): + class _ValidateMessage( + _BaseSchemaServiceRestTransport._BaseValidateMessage, SchemaServiceRestStub + ): def __hash__(self): - return hash("ValidateMessage") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SchemaServiceRestTransport.ValidateMessage") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -1253,47 +1281,34 @@ def __call__( """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{parent=projects/*}/schemas:validateMessage", - "body": "*", - }, - ] + http_options = ( + _BaseSchemaServiceRestTransport._BaseValidateMessage._get_http_options() + ) request, metadata = self._interceptor.pre_validate_message( request, metadata ) - pb_request = schema.ValidateMessageRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = _BaseSchemaServiceRestTransport._BaseValidateMessage._get_transcoded_request( + http_options, request + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = _BaseSchemaServiceRestTransport._BaseValidateMessage._get_request_body_json( + transcoded_request ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BaseSchemaServiceRestTransport._BaseValidateMessage._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = SchemaServiceRestTransport._ValidateMessage._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1309,19 +1324,34 @@ def __call__( resp = self._interceptor.post_validate_message(resp) return resp - class _ValidateSchema(SchemaServiceRestStub): + class _ValidateSchema( + _BaseSchemaServiceRestTransport._BaseValidateSchema, SchemaServiceRestStub + ): def __hash__(self): - return hash("ValidateSchema") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SchemaServiceRestTransport.ValidateSchema") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -1349,45 +1379,32 @@ def __call__( """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{parent=projects/*}/schemas:validate", - "body": "*", - }, - ] + http_options = ( + _BaseSchemaServiceRestTransport._BaseValidateSchema._get_http_options() + ) request, metadata = self._interceptor.pre_validate_schema(request, metadata) - pb_request = gp_schema.ValidateSchemaRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = _BaseSchemaServiceRestTransport._BaseValidateSchema._get_transcoded_request( + http_options, request + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = _BaseSchemaServiceRestTransport._BaseValidateSchema._get_request_body_json( + transcoded_request ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BaseSchemaServiceRestTransport._BaseValidateSchema._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = SchemaServiceRestTransport._ValidateSchema._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1485,7 +1502,34 @@ def validate_schema( def get_iam_policy(self): return self._GetIamPolicy(self._session, self._host, self._interceptor) # type: ignore - class _GetIamPolicy(SchemaServiceRestStub): + class _GetIamPolicy( + _BaseSchemaServiceRestTransport._BaseGetIamPolicy, SchemaServiceRestStub + ): + def __hash__(self): + return hash("SchemaServiceRestTransport.GetIamPolicy") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response + def __call__( self, request: iam_policy_pb2.GetIamPolicyRequest, @@ -1509,44 +1553,27 @@ def __call__( policy_pb2.Policy: Response from GetIamPolicy method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "get", - "uri": "/v1/{resource=projects/*/topics/*}:getIamPolicy", - }, - { - "method": "get", - "uri": "/v1/{resource=projects/*/subscriptions/*}:getIamPolicy", - }, - { - "method": "get", - "uri": "/v1/{resource=projects/*/snapshots/*}:getIamPolicy", - }, - { - "method": "get", - "uri": "/v1/{resource=projects/*/schemas/*}:getIamPolicy", - }, - ] - + http_options = ( + _BaseSchemaServiceRestTransport._BaseGetIamPolicy._get_http_options() + ) request, metadata = self._interceptor.pre_get_iam_policy(request, metadata) - request_kwargs = json_format.MessageToDict(request) - transcoded_request = path_template.transcode(http_options, **request_kwargs) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = _BaseSchemaServiceRestTransport._BaseGetIamPolicy._get_transcoded_request( + http_options, request + ) # Jsonify the query params - query_params = json.loads(json.dumps(transcoded_request["query_params"])) + query_params = _BaseSchemaServiceRestTransport._BaseGetIamPolicy._get_query_params_json( + transcoded_request + ) # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params), + response = SchemaServiceRestTransport._GetIamPolicy._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1554,8 +1581,9 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) + content = response.content.decode("utf-8") resp = policy_pb2.Policy() - resp = json_format.Parse(response.content.decode("utf-8"), resp) + resp = json_format.Parse(content, resp) resp = self._interceptor.post_get_iam_policy(resp) return resp @@ -1563,7 +1591,35 @@ def __call__( def set_iam_policy(self): return self._SetIamPolicy(self._session, self._host, self._interceptor) # type: ignore - class _SetIamPolicy(SchemaServiceRestStub): + class _SetIamPolicy( + _BaseSchemaServiceRestTransport._BaseSetIamPolicy, SchemaServiceRestStub + ): + def __hash__(self): + return hash("SchemaServiceRestTransport.SetIamPolicy") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response + def __call__( self, request: iam_policy_pb2.SetIamPolicyRequest, @@ -1587,50 +1643,32 @@ def __call__( policy_pb2.Policy: Response from SetIamPolicy method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{resource=projects/*/topics/*}:setIamPolicy", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/subscriptions/*}:setIamPolicy", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/snapshots/*}:setIamPolicy", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/schemas/*}:setIamPolicy", - "body": "*", - }, - ] - + http_options = ( + _BaseSchemaServiceRestTransport._BaseSetIamPolicy._get_http_options() + ) request, metadata = self._interceptor.pre_set_iam_policy(request, metadata) - request_kwargs = json_format.MessageToDict(request) - transcoded_request = path_template.transcode(http_options, **request_kwargs) + transcoded_request = _BaseSchemaServiceRestTransport._BaseSetIamPolicy._get_transcoded_request( + http_options, request + ) - body = json.dumps(transcoded_request["body"]) - uri = transcoded_request["uri"] - method = transcoded_request["method"] + body = _BaseSchemaServiceRestTransport._BaseSetIamPolicy._get_request_body_json( + transcoded_request + ) # Jsonify the query params - query_params = json.loads(json.dumps(transcoded_request["query_params"])) + query_params = _BaseSchemaServiceRestTransport._BaseSetIamPolicy._get_query_params_json( + transcoded_request + ) # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params), - data=body, + response = SchemaServiceRestTransport._SetIamPolicy._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1638,8 +1676,9 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) + content = response.content.decode("utf-8") resp = policy_pb2.Policy() - resp = json_format.Parse(response.content.decode("utf-8"), resp) + resp = json_format.Parse(content, resp) resp = self._interceptor.post_set_iam_policy(resp) return resp @@ -1647,7 +1686,35 @@ def __call__( def test_iam_permissions(self): return self._TestIamPermissions(self._session, self._host, self._interceptor) # type: ignore - class _TestIamPermissions(SchemaServiceRestStub): + class _TestIamPermissions( + _BaseSchemaServiceRestTransport._BaseTestIamPermissions, SchemaServiceRestStub + ): + def __hash__(self): + return hash("SchemaServiceRestTransport.TestIamPermissions") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response + def __call__( self, request: iam_policy_pb2.TestIamPermissionsRequest, @@ -1671,52 +1738,34 @@ def __call__( iam_policy_pb2.TestIamPermissionsResponse: Response from TestIamPermissions method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{resource=projects/*/subscriptions/*}:testIamPermissions", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/topics/*}:testIamPermissions", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/snapshots/*}:testIamPermissions", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/schemas/*}:testIamPermissions", - "body": "*", - }, - ] - + http_options = ( + _BaseSchemaServiceRestTransport._BaseTestIamPermissions._get_http_options() + ) request, metadata = self._interceptor.pre_test_iam_permissions( request, metadata ) - request_kwargs = json_format.MessageToDict(request) - transcoded_request = path_template.transcode(http_options, **request_kwargs) + transcoded_request = _BaseSchemaServiceRestTransport._BaseTestIamPermissions._get_transcoded_request( + http_options, request + ) - body = json.dumps(transcoded_request["body"]) - uri = transcoded_request["uri"] - method = transcoded_request["method"] + body = _BaseSchemaServiceRestTransport._BaseTestIamPermissions._get_request_body_json( + transcoded_request + ) # Jsonify the query params - query_params = json.loads(json.dumps(transcoded_request["query_params"])) + query_params = _BaseSchemaServiceRestTransport._BaseTestIamPermissions._get_query_params_json( + transcoded_request + ) # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params), - data=body, + response = SchemaServiceRestTransport._TestIamPermissions._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1724,8 +1773,9 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) + content = response.content.decode("utf-8") resp = iam_policy_pb2.TestIamPermissionsResponse() - resp = json_format.Parse(response.content.decode("utf-8"), resp) + resp = json_format.Parse(content, resp) resp = self._interceptor.post_test_iam_permissions(resp) return resp diff --git a/google/pubsub_v1/services/schema_service/transports/rest_base.py b/google/pubsub_v1/services/schema_service/transports/rest_base.py new file mode 100644 index 000000000..a97e454d4 --- /dev/null +++ b/google/pubsub_v1/services/schema_service/transports/rest_base.py @@ -0,0 +1,748 @@ +# -*- coding: utf-8 -*- +# 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. +# 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 json # type: ignore +from google.api_core import path_template +from google.api_core import gapic_v1 + +from google.protobuf import json_format +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from .base import SchemaServiceTransport, DEFAULT_CLIENT_INFO + +import re +from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union + + +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.protobuf import empty_pb2 # type: ignore +from google.pubsub_v1.types import schema +from google.pubsub_v1.types import schema as gp_schema + + +class _BaseSchemaServiceRestTransport(SchemaServiceTransport): + """Base REST backend transport for SchemaService. + + Note: This class is not meant to be used directly. Use its sync and + async sub-classes instead. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends JSON representations of protocol buffers over HTTP/1.1 + """ + + def __init__( + self, + *, + host: str = "pubsub.googleapis.com", + credentials: Optional[Any] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + url_scheme: str = "https", + api_audience: Optional[str] = None, + ) -> None: + """Instantiate the transport. + Args: + host (Optional[str]): + The hostname to connect to (default: 'pubsub.googleapis.com'). + credentials (Optional[Any]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you are developing + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + url_scheme: the protocol scheme for the API endpoint. Normally + "https", but for testing or local servers, + "http" can be specified. + """ + # Run the base constructor + maybe_url_match = re.match("^(?Phttp(?:s)?://)?(?P.*)$", host) + if maybe_url_match is None: + raise ValueError( + f"Unexpected hostname structure: {host}" + ) # pragma: NO COVER + + url_match_items = maybe_url_match.groupdict() + + host = f"{url_scheme}://{host}" if not url_match_items["scheme"] else host + + super().__init__( + host=host, + credentials=credentials, + client_info=client_info, + always_use_jwt_access=always_use_jwt_access, + api_audience=api_audience, + ) + + class _BaseCommitSchema: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{name=projects/*/schemas/*}:commit", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = gp_schema.CommitSchemaRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSchemaServiceRestTransport._BaseCommitSchema._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseCreateSchema: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{parent=projects/*}/schemas", + "body": "schema", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = gp_schema.CreateSchemaRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSchemaServiceRestTransport._BaseCreateSchema._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseDeleteSchema: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "delete", + "uri": "/v1/{name=projects/*/schemas/*}", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = schema.DeleteSchemaRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSchemaServiceRestTransport._BaseDeleteSchema._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseDeleteSchemaRevision: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "delete", + "uri": "/v1/{name=projects/*/schemas/*}:deleteRevision", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = schema.DeleteSchemaRevisionRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSchemaServiceRestTransport._BaseDeleteSchemaRevision._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseGetSchema: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{name=projects/*/schemas/*}", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = schema.GetSchemaRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSchemaServiceRestTransport._BaseGetSchema._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseListSchemaRevisions: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{name=projects/*/schemas/*}:listRevisions", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = schema.ListSchemaRevisionsRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSchemaServiceRestTransport._BaseListSchemaRevisions._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseListSchemas: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{parent=projects/*}/schemas", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = schema.ListSchemasRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSchemaServiceRestTransport._BaseListSchemas._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseRollbackSchema: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{name=projects/*/schemas/*}:rollback", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = schema.RollbackSchemaRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSchemaServiceRestTransport._BaseRollbackSchema._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseValidateMessage: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{parent=projects/*}/schemas:validateMessage", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = schema.ValidateMessageRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSchemaServiceRestTransport._BaseValidateMessage._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseValidateSchema: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{parent=projects/*}/schemas:validate", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = gp_schema.ValidateSchemaRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSchemaServiceRestTransport._BaseValidateSchema._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseGetIamPolicy: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{resource=projects/*/topics/*}:getIamPolicy", + }, + { + "method": "get", + "uri": "/v1/{resource=projects/*/subscriptions/*}:getIamPolicy", + }, + { + "method": "get", + "uri": "/v1/{resource=projects/*/snapshots/*}:getIamPolicy", + }, + { + "method": "get", + "uri": "/v1/{resource=projects/*/schemas/*}:getIamPolicy", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + request_kwargs = json_format.MessageToDict(request) + transcoded_request = path_template.transcode(http_options, **request_kwargs) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads(json.dumps(transcoded_request["query_params"])) + return query_params + + class _BaseSetIamPolicy: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{resource=projects/*/topics/*}:setIamPolicy", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/subscriptions/*}:setIamPolicy", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/snapshots/*}:setIamPolicy", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/schemas/*}:setIamPolicy", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + request_kwargs = json_format.MessageToDict(request) + transcoded_request = path_template.transcode(http_options, **request_kwargs) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + body = json.dumps(transcoded_request["body"]) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads(json.dumps(transcoded_request["query_params"])) + return query_params + + class _BaseTestIamPermissions: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{resource=projects/*/subscriptions/*}:testIamPermissions", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/topics/*}:testIamPermissions", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/snapshots/*}:testIamPermissions", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/schemas/*}:testIamPermissions", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + request_kwargs = json_format.MessageToDict(request) + transcoded_request = path_template.transcode(http_options, **request_kwargs) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + body = json.dumps(transcoded_request["body"]) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads(json.dumps(transcoded_request["query_params"])) + return query_params + + +__all__ = ("_BaseSchemaServiceRestTransport",) diff --git a/google/pubsub_v1/services/subscriber/async_client.py b/google/pubsub_v1/services/subscriber/async_client.py index dcd4f0bbb..7f40480ef 100644 --- a/google/pubsub_v1/services/subscriber/async_client.py +++ b/google/pubsub_v1/services/subscriber/async_client.py @@ -2309,11 +2309,7 @@ async def set_iam_policy( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.set_iam_policy, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self.transport._wrapped_methods[self._client._transport.set_iam_policy] # Certain fields should be provided within the metadata header; # add these here. @@ -2433,11 +2429,7 @@ async def get_iam_policy( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.get_iam_policy, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self.transport._wrapped_methods[self._client._transport.get_iam_policy] # Certain fields should be provided within the metadata header; # add these here. @@ -2495,11 +2487,9 @@ async def test_iam_permissions( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method_async.wrap_method( - self._client._transport.test_iam_permissions, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self.transport._wrapped_methods[ + self._client._transport.test_iam_permissions + ] # Certain fields should be provided within the metadata header; # add these here. diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index 9bad804c6..d601b0f0d 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -515,36 +515,6 @@ def _get_universe_domain( raise ValueError("Universe Domain cannot be an empty string.") return universe_domain - @staticmethod - def _compare_universes( - client_universe: str, credentials: ga_credentials.Credentials - ) -> bool: - """Returns True iff the universe domains used by the client and credentials match. - - Args: - client_universe (str): The universe domain configured via the client options. - credentials (ga_credentials.Credentials): The credentials being used in the client. - - Returns: - bool: True iff client_universe matches the universe in credentials. - - Raises: - ValueError: when client_universe does not match the universe in credentials. - """ - - default_universe = SubscriberClient._DEFAULT_UNIVERSE - credentials_universe = getattr(credentials, "universe_domain", default_universe) - - if client_universe != credentials_universe: - raise ValueError( - "The configured universe domain " - f"({client_universe}) does not match the universe domain " - f"found in the credentials ({credentials_universe}). " - "If you haven't configured the universe domain explicitly, " - f"`{default_universe}` is the default." - ) - return True - def _validate_universe_domain(self): """Validates client's and credentials' universe domains are consistent. @@ -554,13 +524,9 @@ def _validate_universe_domain(self): Raises: ValueError: If the configured universe domain is not valid. """ - self._is_universe_domain_valid = ( - self._is_universe_domain_valid - or SubscriberClient._compare_universes( - self.universe_domain, self.transport._credentials - ) - ) - return self._is_universe_domain_valid + + # NOTE (b/349488459): universe validation is disabled until further notice. + return True @property def api_endpoint(self): diff --git a/google/pubsub_v1/services/subscriber/transports/README.rst b/google/pubsub_v1/services/subscriber/transports/README.rst new file mode 100644 index 000000000..2df98ffe6 --- /dev/null +++ b/google/pubsub_v1/services/subscriber/transports/README.rst @@ -0,0 +1,9 @@ + +transport inheritance structure +_______________________________ + +`SubscriberTransport` is the ABC for all transports. +- public child `SubscriberGrpcTransport` for sync gRPC transport (defined in `grpc.py`). +- public child `SubscriberGrpcAsyncIOTransport` for async gRPC transport (defined in `grpc_asyncio.py`). +- private child `_BaseSubscriberRestTransport` for base REST transport with inner classes `_BaseMETHOD` (defined in `rest_base.py`). +- public child `SubscriberRestTransport` for sync REST transport with inner classes `METHOD` derived from the parent's corresponding `_BaseMETHOD` classes (defined in `rest.py`). diff --git a/google/pubsub_v1/services/subscriber/transports/base.py b/google/pubsub_v1/services/subscriber/transports/base.py index c5fa183b1..51b50a55f 100644 --- a/google/pubsub_v1/services/subscriber/transports/base.py +++ b/google/pubsub_v1/services/subscriber/transports/base.py @@ -378,6 +378,21 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), + self.get_iam_policy: gapic_v1.method.wrap_method( + self.get_iam_policy, + default_timeout=None, + client_info=client_info, + ), + self.set_iam_policy: gapic_v1.method.wrap_method( + self.set_iam_policy, + default_timeout=None, + client_info=client_info, + ), + self.test_iam_permissions: gapic_v1.method.wrap_method( + self.test_iam_permissions, + default_timeout=None, + client_info=client_info, + ), } def close(self): diff --git a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py index 9dab4a21b..de9606858 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import inspect import warnings from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union @@ -235,6 +236,9 @@ def __init__( ) # Wrap messages. This must be done after self._grpc_channel exists + self._wrap_with_kind = ( + "kind" in inspect.signature(gapic_v1.method_async.wrap_method).parameters + ) self._prep_wrapped_messages(client_info) @property @@ -842,7 +846,7 @@ def test_iam_permissions( def _prep_wrapped_messages(self, client_info): """Precompute the wrapped methods, overriding the base class method to use async wrappers.""" self._wrapped_methods = { - self.create_subscription: gapic_v1.method_async.wrap_method( + self.create_subscription: self._wrap_method( self.create_subscription, default_retry=retries.AsyncRetry( initial=0.1, @@ -858,7 +862,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.get_subscription: gapic_v1.method_async.wrap_method( + self.get_subscription: self._wrap_method( self.get_subscription, default_retry=retries.AsyncRetry( initial=0.1, @@ -874,7 +878,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.update_subscription: gapic_v1.method_async.wrap_method( + self.update_subscription: self._wrap_method( self.update_subscription, default_retry=retries.AsyncRetry( initial=0.1, @@ -888,7 +892,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.list_subscriptions: gapic_v1.method_async.wrap_method( + self.list_subscriptions: self._wrap_method( self.list_subscriptions, default_retry=retries.AsyncRetry( initial=0.1, @@ -904,7 +908,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.delete_subscription: gapic_v1.method_async.wrap_method( + self.delete_subscription: self._wrap_method( self.delete_subscription, default_retry=retries.AsyncRetry( initial=0.1, @@ -918,7 +922,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.modify_ack_deadline: gapic_v1.method_async.wrap_method( + self.modify_ack_deadline: self._wrap_method( self.modify_ack_deadline, default_retry=retries.AsyncRetry( initial=0.1, @@ -932,7 +936,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.acknowledge: gapic_v1.method_async.wrap_method( + self.acknowledge: self._wrap_method( self.acknowledge, default_retry=retries.AsyncRetry( initial=0.1, @@ -946,7 +950,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.pull: gapic_v1.method_async.wrap_method( + self.pull: self._wrap_method( self.pull, default_retry=retries.AsyncRetry( initial=0.1, @@ -963,7 +967,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.streaming_pull: gapic_v1.method_async.wrap_method( + self.streaming_pull: self._wrap_method( self.streaming_pull, default_retry=retries.AsyncRetry( initial=0.1, @@ -981,7 +985,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=900.0, client_info=client_info, ), - self.modify_push_config: gapic_v1.method_async.wrap_method( + self.modify_push_config: self._wrap_method( self.modify_push_config, default_retry=retries.AsyncRetry( initial=0.1, @@ -995,7 +999,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.get_snapshot: gapic_v1.method_async.wrap_method( + self.get_snapshot: self._wrap_method( self.get_snapshot, default_retry=retries.AsyncRetry( initial=0.1, @@ -1011,7 +1015,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.list_snapshots: gapic_v1.method_async.wrap_method( + self.list_snapshots: self._wrap_method( self.list_snapshots, default_retry=retries.AsyncRetry( initial=0.1, @@ -1027,7 +1031,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.create_snapshot: gapic_v1.method_async.wrap_method( + self.create_snapshot: self._wrap_method( self.create_snapshot, default_retry=retries.AsyncRetry( initial=0.1, @@ -1041,7 +1045,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.update_snapshot: gapic_v1.method_async.wrap_method( + self.update_snapshot: self._wrap_method( self.update_snapshot, default_retry=retries.AsyncRetry( initial=0.1, @@ -1055,7 +1059,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.delete_snapshot: gapic_v1.method_async.wrap_method( + self.delete_snapshot: self._wrap_method( self.delete_snapshot, default_retry=retries.AsyncRetry( initial=0.1, @@ -1069,7 +1073,7 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), - self.seek: gapic_v1.method_async.wrap_method( + self.seek: self._wrap_method( self.seek, default_retry=retries.AsyncRetry( initial=0.1, @@ -1085,10 +1089,34 @@ def _prep_wrapped_messages(self, client_info): default_timeout=60.0, client_info=client_info, ), + self.get_iam_policy: self._wrap_method( + self.get_iam_policy, + default_timeout=None, + client_info=client_info, + ), + self.set_iam_policy: self._wrap_method( + self.set_iam_policy, + default_timeout=None, + client_info=client_info, + ), + self.test_iam_permissions: self._wrap_method( + self.test_iam_permissions, + default_timeout=None, + client_info=client_info, + ), } + def _wrap_method(self, func, *args, **kwargs): + if self._wrap_with_kind: # pragma: NO COVER + kwargs["kind"] = self.kind + return gapic_v1.method_async.wrap_method(func, *args, **kwargs) + def close(self): return self.grpc_channel.close() + @property + def kind(self) -> str: + return "grpc_asyncio" + __all__ = ("SubscriberGrpcAsyncIOTransport",) diff --git a/google/pubsub_v1/services/subscriber/transports/rest.py b/google/pubsub_v1/services/subscriber/transports/rest.py index 0b5f2ccc9..376fd4ab3 100644 --- a/google/pubsub_v1/services/subscriber/transports/rest.py +++ b/google/pubsub_v1/services/subscriber/transports/rest.py @@ -16,43 +16,42 @@ from google.auth.transport.requests import AuthorizedSession # type: ignore import json # type: ignore -import grpc # type: ignore -from google.auth.transport.grpc import SslCredentials # type: ignore from google.auth import credentials as ga_credentials # type: ignore from google.api_core import exceptions as core_exceptions from google.api_core import retry as retries from google.api_core import rest_helpers from google.api_core import rest_streaming -from google.api_core import path_template from google.api_core import gapic_v1 from google.protobuf import json_format from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore + from requests import __version__ as requests_version import dataclasses -import re from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union import warnings -try: - OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] -except AttributeError: # pragma: NO COVER - OptionalRetry = Union[retries.Retry, object, None] # type: ignore - from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore from google.protobuf import empty_pb2 # type: ignore from google.pubsub_v1.types import pubsub -from .base import SubscriberTransport, DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO + +from .rest_base import _BaseSubscriberRestTransport +from .base import DEFAULT_CLIENT_INFO as BASE_DEFAULT_CLIENT_INFO + +try: + OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] +except AttributeError: # pragma: NO COVER + OptionalRetry = Union[retries.Retry, object, None] # type: ignore DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version, grpc_version=None, - rest_version=requests_version, + rest_version=f"requests@{requests_version}", ) @@ -512,8 +511,8 @@ class SubscriberRestStub: _interceptor: SubscriberRestInterceptor -class SubscriberRestTransport(SubscriberTransport): - """REST backend transport for Subscriber. +class SubscriberRestTransport(_BaseSubscriberRestTransport): + """REST backend synchronous transport for Subscriber. The service that an application uses to manipulate subscriptions and to consume messages from a subscription via the ``Pull`` method or @@ -525,7 +524,6 @@ class SubscriberRestTransport(SubscriberTransport): and call it. It sends JSON representations of protocol buffers over HTTP/1.1 - """ def __init__( @@ -579,21 +577,12 @@ def __init__( # TODO(yon-mg): resolve other ctor params i.e. scopes, quota, etc. # TODO: When custom host (api_endpoint) is set, `scopes` must *also* be set on the # credentials object - maybe_url_match = re.match("^(?Phttp(?:s)?://)?(?P.*)$", host) - if maybe_url_match is None: - raise ValueError( - f"Unexpected hostname structure: {host}" - ) # pragma: NO COVER - - url_match_items = maybe_url_match.groupdict() - - host = f"{url_scheme}://{host}" if not url_match_items["scheme"] else host - super().__init__( host=host, credentials=credentials, client_info=client_info, always_use_jwt_access=always_use_jwt_access, + url_scheme=url_scheme, api_audience=api_audience, ) self._session = AuthorizedSession( @@ -604,19 +593,34 @@ def __init__( self._interceptor = interceptor or SubscriberRestInterceptor() self._prep_wrapped_messages(client_info) - class _Acknowledge(SubscriberRestStub): + class _Acknowledge( + _BaseSubscriberRestTransport._BaseAcknowledge, SubscriberRestStub + ): def __hash__(self): - return hash("Acknowledge") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SubscriberRestTransport.Acknowledge") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -638,45 +642,36 @@ def __call__( sent along with the request as metadata. """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{subscription=projects/*/subscriptions/*}:acknowledge", - "body": "*", - }, - ] + http_options = ( + _BaseSubscriberRestTransport._BaseAcknowledge._get_http_options() + ) request, metadata = self._interceptor.pre_acknowledge(request, metadata) - pb_request = pubsub.AcknowledgeRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = ( + _BaseSubscriberRestTransport._BaseAcknowledge._get_transcoded_request( + http_options, request + ) + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = _BaseSubscriberRestTransport._BaseAcknowledge._get_request_body_json( + transcoded_request ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BaseSubscriberRestTransport._BaseAcknowledge._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = SubscriberRestTransport._Acknowledge._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -684,19 +679,34 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) - class _CreateSnapshot(SubscriberRestStub): + class _CreateSnapshot( + _BaseSubscriberRestTransport._BaseCreateSnapshot, SubscriberRestStub + ): def __hash__(self): - return hash("CreateSnapshot") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SubscriberRestTransport.CreateSnapshot") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -728,45 +738,36 @@ def __call__( """ - http_options: List[Dict[str, str]] = [ - { - "method": "put", - "uri": "/v1/{name=projects/*/snapshots/*}", - "body": "*", - }, - ] + http_options = ( + _BaseSubscriberRestTransport._BaseCreateSnapshot._get_http_options() + ) request, metadata = self._interceptor.pre_create_snapshot(request, metadata) - pb_request = pubsub.CreateSnapshotRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = _BaseSubscriberRestTransport._BaseCreateSnapshot._get_transcoded_request( + http_options, request + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = ( + _BaseSubscriberRestTransport._BaseCreateSnapshot._get_request_body_json( + transcoded_request + ) ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BaseSubscriberRestTransport._BaseCreateSnapshot._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = SubscriberRestTransport._CreateSnapshot._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -782,19 +783,34 @@ def __call__( resp = self._interceptor.post_create_snapshot(resp) return resp - class _CreateSubscription(SubscriberRestStub): + class _CreateSubscription( + _BaseSubscriberRestTransport._BaseCreateSubscription, SubscriberRestStub + ): def __hash__(self): - return hash("CreateSubscription") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SubscriberRestTransport.CreateSubscription") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -827,47 +843,34 @@ def __call__( """ - http_options: List[Dict[str, str]] = [ - { - "method": "put", - "uri": "/v1/{name=projects/*/subscriptions/*}", - "body": "*", - }, - ] + http_options = ( + _BaseSubscriberRestTransport._BaseCreateSubscription._get_http_options() + ) request, metadata = self._interceptor.pre_create_subscription( request, metadata ) - pb_request = pubsub.Subscription.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = _BaseSubscriberRestTransport._BaseCreateSubscription._get_transcoded_request( + http_options, request + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = _BaseSubscriberRestTransport._BaseCreateSubscription._get_request_body_json( + transcoded_request ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BaseSubscriberRestTransport._BaseCreateSubscription._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = SubscriberRestTransport._CreateSubscription._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -883,19 +886,33 @@ def __call__( resp = self._interceptor.post_create_subscription(resp) return resp - class _DeleteSnapshot(SubscriberRestStub): + class _DeleteSnapshot( + _BaseSubscriberRestTransport._BaseDeleteSnapshot, SubscriberRestStub + ): def __hash__(self): - return hash("DeleteSnapshot") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SubscriberRestTransport.DeleteSnapshot") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -917,38 +934,29 @@ def __call__( sent along with the request as metadata. """ - http_options: List[Dict[str, str]] = [ - { - "method": "delete", - "uri": "/v1/{snapshot=projects/*/snapshots/*}", - }, - ] + http_options = ( + _BaseSubscriberRestTransport._BaseDeleteSnapshot._get_http_options() + ) request, metadata = self._interceptor.pre_delete_snapshot(request, metadata) - pb_request = pubsub.DeleteSnapshotRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = _BaseSubscriberRestTransport._BaseDeleteSnapshot._get_transcoded_request( + http_options, request + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BaseSubscriberRestTransport._BaseDeleteSnapshot._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = SubscriberRestTransport._DeleteSnapshot._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -956,19 +964,33 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) - class _DeleteSubscription(SubscriberRestStub): + class _DeleteSubscription( + _BaseSubscriberRestTransport._BaseDeleteSubscription, SubscriberRestStub + ): def __hash__(self): - return hash("DeleteSubscription") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SubscriberRestTransport.DeleteSubscription") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -991,40 +1013,29 @@ def __call__( sent along with the request as metadata. """ - http_options: List[Dict[str, str]] = [ - { - "method": "delete", - "uri": "/v1/{subscription=projects/*/subscriptions/*}", - }, - ] + http_options = ( + _BaseSubscriberRestTransport._BaseDeleteSubscription._get_http_options() + ) request, metadata = self._interceptor.pre_delete_subscription( request, metadata ) - pb_request = pubsub.DeleteSubscriptionRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = _BaseSubscriberRestTransport._BaseDeleteSubscription._get_transcoded_request( + http_options, request + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BaseSubscriberRestTransport._BaseDeleteSubscription._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = SubscriberRestTransport._DeleteSubscription._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1032,19 +1043,33 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) - class _GetSnapshot(SubscriberRestStub): + class _GetSnapshot( + _BaseSubscriberRestTransport._BaseGetSnapshot, SubscriberRestStub + ): def __hash__(self): - return hash("GetSnapshot") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SubscriberRestTransport.GetSnapshot") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -1076,38 +1101,31 @@ def __call__( """ - http_options: List[Dict[str, str]] = [ - { - "method": "get", - "uri": "/v1/{snapshot=projects/*/snapshots/*}", - }, - ] + http_options = ( + _BaseSubscriberRestTransport._BaseGetSnapshot._get_http_options() + ) request, metadata = self._interceptor.pre_get_snapshot(request, metadata) - pb_request = pubsub.GetSnapshotRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = ( + _BaseSubscriberRestTransport._BaseGetSnapshot._get_transcoded_request( + http_options, request + ) + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BaseSubscriberRestTransport._BaseGetSnapshot._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = SubscriberRestTransport._GetSnapshot._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1123,19 +1141,33 @@ def __call__( resp = self._interceptor.post_get_snapshot(resp) return resp - class _GetSubscription(SubscriberRestStub): + class _GetSubscription( + _BaseSubscriberRestTransport._BaseGetSubscription, SubscriberRestStub + ): def __hash__(self): - return hash("GetSubscription") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SubscriberRestTransport.GetSubscription") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -1166,40 +1198,29 @@ def __call__( """ - http_options: List[Dict[str, str]] = [ - { - "method": "get", - "uri": "/v1/{subscription=projects/*/subscriptions/*}", - }, - ] + http_options = ( + _BaseSubscriberRestTransport._BaseGetSubscription._get_http_options() + ) request, metadata = self._interceptor.pre_get_subscription( request, metadata ) - pb_request = pubsub.GetSubscriptionRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = _BaseSubscriberRestTransport._BaseGetSubscription._get_transcoded_request( + http_options, request + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BaseSubscriberRestTransport._BaseGetSubscription._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = SubscriberRestTransport._GetSubscription._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1215,19 +1236,33 @@ def __call__( resp = self._interceptor.post_get_subscription(resp) return resp - class _ListSnapshots(SubscriberRestStub): + class _ListSnapshots( + _BaseSubscriberRestTransport._BaseListSnapshots, SubscriberRestStub + ): def __hash__(self): - return hash("ListSnapshots") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SubscriberRestTransport.ListSnapshots") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -1253,38 +1288,31 @@ def __call__( Response for the ``ListSnapshots`` method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "get", - "uri": "/v1/{project=projects/*}/snapshots", - }, - ] + http_options = ( + _BaseSubscriberRestTransport._BaseListSnapshots._get_http_options() + ) request, metadata = self._interceptor.pre_list_snapshots(request, metadata) - pb_request = pubsub.ListSnapshotsRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = ( + _BaseSubscriberRestTransport._BaseListSnapshots._get_transcoded_request( + http_options, request + ) + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BaseSubscriberRestTransport._BaseListSnapshots._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = SubscriberRestTransport._ListSnapshots._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1300,19 +1328,33 @@ def __call__( resp = self._interceptor.post_list_snapshots(resp) return resp - class _ListSubscriptions(SubscriberRestStub): + class _ListSubscriptions( + _BaseSubscriberRestTransport._BaseListSubscriptions, SubscriberRestStub + ): def __hash__(self): - return hash("ListSubscriptions") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SubscriberRestTransport.ListSubscriptions") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response def __call__( self, @@ -1338,40 +1380,29 @@ def __call__( Response for the ``ListSubscriptions`` method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "get", - "uri": "/v1/{project=projects/*}/subscriptions", - }, - ] + http_options = ( + _BaseSubscriberRestTransport._BaseListSubscriptions._get_http_options() + ) request, metadata = self._interceptor.pre_list_subscriptions( request, metadata ) - pb_request = pubsub.ListSubscriptionsRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = _BaseSubscriberRestTransport._BaseListSubscriptions._get_transcoded_request( + http_options, request + ) # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BaseSubscriberRestTransport._BaseListSubscriptions._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), + response = SubscriberRestTransport._ListSubscriptions._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1387,19 +1418,34 @@ def __call__( resp = self._interceptor.post_list_subscriptions(resp) return resp - class _ModifyAckDeadline(SubscriberRestStub): + class _ModifyAckDeadline( + _BaseSubscriberRestTransport._BaseModifyAckDeadline, SubscriberRestStub + ): def __hash__(self): - return hash("ModifyAckDeadline") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SubscriberRestTransport.ModifyAckDeadline") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -1422,47 +1468,34 @@ def __call__( sent along with the request as metadata. """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{subscription=projects/*/subscriptions/*}:modifyAckDeadline", - "body": "*", - }, - ] + http_options = ( + _BaseSubscriberRestTransport._BaseModifyAckDeadline._get_http_options() + ) request, metadata = self._interceptor.pre_modify_ack_deadline( request, metadata ) - pb_request = pubsub.ModifyAckDeadlineRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = _BaseSubscriberRestTransport._BaseModifyAckDeadline._get_transcoded_request( + http_options, request + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = _BaseSubscriberRestTransport._BaseModifyAckDeadline._get_request_body_json( + transcoded_request ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BaseSubscriberRestTransport._BaseModifyAckDeadline._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = SubscriberRestTransport._ModifyAckDeadline._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1470,19 +1503,34 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) - class _ModifyPushConfig(SubscriberRestStub): + class _ModifyPushConfig( + _BaseSubscriberRestTransport._BaseModifyPushConfig, SubscriberRestStub + ): def __hash__(self): - return hash("ModifyPushConfig") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SubscriberRestTransport.ModifyPushConfig") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -1505,47 +1553,34 @@ def __call__( sent along with the request as metadata. """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{subscription=projects/*/subscriptions/*}:modifyPushConfig", - "body": "*", - }, - ] + http_options = ( + _BaseSubscriberRestTransport._BaseModifyPushConfig._get_http_options() + ) request, metadata = self._interceptor.pre_modify_push_config( request, metadata ) - pb_request = pubsub.ModifyPushConfigRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = _BaseSubscriberRestTransport._BaseModifyPushConfig._get_transcoded_request( + http_options, request + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = _BaseSubscriberRestTransport._BaseModifyPushConfig._get_request_body_json( + transcoded_request ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BaseSubscriberRestTransport._BaseModifyPushConfig._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = SubscriberRestTransport._ModifyPushConfig._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1553,19 +1588,32 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) - class _Pull(SubscriberRestStub): + class _Pull(_BaseSubscriberRestTransport._BasePull, SubscriberRestStub): def __hash__(self): - return hash("Pull") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SubscriberRestTransport.Pull") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -1591,45 +1639,34 @@ def __call__( Response for the ``Pull`` method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{subscription=projects/*/subscriptions/*}:pull", - "body": "*", - }, - ] + http_options = _BaseSubscriberRestTransport._BasePull._get_http_options() request, metadata = self._interceptor.pre_pull(request, metadata) - pb_request = pubsub.PullRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = ( + _BaseSubscriberRestTransport._BasePull._get_transcoded_request( + http_options, request + ) + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = _BaseSubscriberRestTransport._BasePull._get_request_body_json( + transcoded_request ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BaseSubscriberRestTransport._BasePull._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = SubscriberRestTransport._Pull._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1645,19 +1682,32 @@ def __call__( resp = self._interceptor.post_pull(resp) return resp - class _Seek(SubscriberRestStub): + class _Seek(_BaseSubscriberRestTransport._BaseSeek, SubscriberRestStub): def __hash__(self): - return hash("Seek") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SubscriberRestTransport.Seek") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -1685,45 +1735,34 @@ def __call__( """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{subscription=projects/*/subscriptions/*}:seek", - "body": "*", - }, - ] + http_options = _BaseSubscriberRestTransport._BaseSeek._get_http_options() request, metadata = self._interceptor.pre_seek(request, metadata) - pb_request = pubsub.SeekRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = ( + _BaseSubscriberRestTransport._BaseSeek._get_transcoded_request( + http_options, request + ) + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = _BaseSubscriberRestTransport._BaseSeek._get_request_body_json( + transcoded_request ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BaseSubscriberRestTransport._BaseSeek._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = SubscriberRestTransport._Seek._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1739,9 +1778,11 @@ def __call__( resp = self._interceptor.post_seek(resp) return resp - class _StreamingPull(SubscriberRestStub): + class _StreamingPull( + _BaseSubscriberRestTransport._BaseStreamingPull, SubscriberRestStub + ): def __hash__(self): - return hash("StreamingPull") + return hash("SubscriberRestTransport.StreamingPull") def __call__( self, @@ -1755,19 +1796,34 @@ def __call__( "Method StreamingPull is not available over REST transport" ) - class _UpdateSnapshot(SubscriberRestStub): + class _UpdateSnapshot( + _BaseSubscriberRestTransport._BaseUpdateSnapshot, SubscriberRestStub + ): def __hash__(self): - return hash("UpdateSnapshot") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SubscriberRestTransport.UpdateSnapshot") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -1800,45 +1856,36 @@ def __call__( """ - http_options: List[Dict[str, str]] = [ - { - "method": "patch", - "uri": "/v1/{snapshot.name=projects/*/snapshots/*}", - "body": "*", - }, - ] + http_options = ( + _BaseSubscriberRestTransport._BaseUpdateSnapshot._get_http_options() + ) request, metadata = self._interceptor.pre_update_snapshot(request, metadata) - pb_request = pubsub.UpdateSnapshotRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = _BaseSubscriberRestTransport._BaseUpdateSnapshot._get_transcoded_request( + http_options, request + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = ( + _BaseSubscriberRestTransport._BaseUpdateSnapshot._get_request_body_json( + transcoded_request + ) ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, + query_params = ( + _BaseSubscriberRestTransport._BaseUpdateSnapshot._get_query_params_json( + transcoded_request ) ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = SubscriberRestTransport._UpdateSnapshot._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -1854,19 +1901,34 @@ def __call__( resp = self._interceptor.post_update_snapshot(resp) return resp - class _UpdateSubscription(SubscriberRestStub): + class _UpdateSubscription( + _BaseSubscriberRestTransport._BaseUpdateSubscription, SubscriberRestStub + ): def __hash__(self): - return hash("UpdateSubscription") - - __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} - - @classmethod - def _get_unset_required_fields(cls, message_dict): - return { - k: v - for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() - if k not in message_dict - } + return hash("SubscriberRestTransport.UpdateSubscription") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response def __call__( self, @@ -1897,47 +1959,34 @@ def __call__( """ - http_options: List[Dict[str, str]] = [ - { - "method": "patch", - "uri": "/v1/{subscription.name=projects/*/subscriptions/*}", - "body": "*", - }, - ] + http_options = ( + _BaseSubscriberRestTransport._BaseUpdateSubscription._get_http_options() + ) request, metadata = self._interceptor.pre_update_subscription( request, metadata ) - pb_request = pubsub.UpdateSubscriptionRequest.pb(request) - transcoded_request = path_template.transcode(http_options, pb_request) - - # Jsonify the request body + transcoded_request = _BaseSubscriberRestTransport._BaseUpdateSubscription._get_transcoded_request( + http_options, request + ) - body = json_format.MessageToJson( - transcoded_request["body"], use_integers_for_enums=True + body = _BaseSubscriberRestTransport._BaseUpdateSubscription._get_request_body_json( + transcoded_request ) - uri = transcoded_request["uri"] - method = transcoded_request["method"] # Jsonify the query params - query_params = json.loads( - json_format.MessageToJson( - transcoded_request["query_params"], - use_integers_for_enums=True, - ) + query_params = _BaseSubscriberRestTransport._BaseUpdateSubscription._get_query_params_json( + transcoded_request ) - query_params.update(self._get_unset_required_fields(query_params)) - - query_params["$alt"] = "json;enum-encoding=int" # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params, strict=True), - data=body, + response = SubscriberRestTransport._UpdateSubscription._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -2077,7 +2126,34 @@ def update_subscription( def get_iam_policy(self): return self._GetIamPolicy(self._session, self._host, self._interceptor) # type: ignore - class _GetIamPolicy(SubscriberRestStub): + class _GetIamPolicy( + _BaseSubscriberRestTransport._BaseGetIamPolicy, SubscriberRestStub + ): + def __hash__(self): + return hash("SubscriberRestTransport.GetIamPolicy") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response + def __call__( self, request: iam_policy_pb2.GetIamPolicyRequest, @@ -2101,44 +2177,31 @@ def __call__( policy_pb2.Policy: Response from GetIamPolicy method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "get", - "uri": "/v1/{resource=projects/*/topics/*}:getIamPolicy", - }, - { - "method": "get", - "uri": "/v1/{resource=projects/*/subscriptions/*}:getIamPolicy", - }, - { - "method": "get", - "uri": "/v1/{resource=projects/*/snapshots/*}:getIamPolicy", - }, - { - "method": "get", - "uri": "/v1/{resource=projects/*/schemas/*}:getIamPolicy", - }, - ] - + http_options = ( + _BaseSubscriberRestTransport._BaseGetIamPolicy._get_http_options() + ) request, metadata = self._interceptor.pre_get_iam_policy(request, metadata) - request_kwargs = json_format.MessageToDict(request) - transcoded_request = path_template.transcode(http_options, **request_kwargs) - - uri = transcoded_request["uri"] - method = transcoded_request["method"] + transcoded_request = ( + _BaseSubscriberRestTransport._BaseGetIamPolicy._get_transcoded_request( + http_options, request + ) + ) # Jsonify the query params - query_params = json.loads(json.dumps(transcoded_request["query_params"])) + query_params = ( + _BaseSubscriberRestTransport._BaseGetIamPolicy._get_query_params_json( + transcoded_request + ) + ) # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params), + response = SubscriberRestTransport._GetIamPolicy._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -2146,8 +2209,9 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) + content = response.content.decode("utf-8") resp = policy_pb2.Policy() - resp = json_format.Parse(response.content.decode("utf-8"), resp) + resp = json_format.Parse(content, resp) resp = self._interceptor.post_get_iam_policy(resp) return resp @@ -2155,7 +2219,35 @@ def __call__( def set_iam_policy(self): return self._SetIamPolicy(self._session, self._host, self._interceptor) # type: ignore - class _SetIamPolicy(SubscriberRestStub): + class _SetIamPolicy( + _BaseSubscriberRestTransport._BaseSetIamPolicy, SubscriberRestStub + ): + def __hash__(self): + return hash("SubscriberRestTransport.SetIamPolicy") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response + def __call__( self, request: iam_policy_pb2.SetIamPolicyRequest, @@ -2179,50 +2271,38 @@ def __call__( policy_pb2.Policy: Response from SetIamPolicy method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{resource=projects/*/topics/*}:setIamPolicy", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/subscriptions/*}:setIamPolicy", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/snapshots/*}:setIamPolicy", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/schemas/*}:setIamPolicy", - "body": "*", - }, - ] - + http_options = ( + _BaseSubscriberRestTransport._BaseSetIamPolicy._get_http_options() + ) request, metadata = self._interceptor.pre_set_iam_policy(request, metadata) - request_kwargs = json_format.MessageToDict(request) - transcoded_request = path_template.transcode(http_options, **request_kwargs) + transcoded_request = ( + _BaseSubscriberRestTransport._BaseSetIamPolicy._get_transcoded_request( + http_options, request + ) + ) - body = json.dumps(transcoded_request["body"]) - uri = transcoded_request["uri"] - method = transcoded_request["method"] + body = ( + _BaseSubscriberRestTransport._BaseSetIamPolicy._get_request_body_json( + transcoded_request + ) + ) # Jsonify the query params - query_params = json.loads(json.dumps(transcoded_request["query_params"])) + query_params = ( + _BaseSubscriberRestTransport._BaseSetIamPolicy._get_query_params_json( + transcoded_request + ) + ) # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params), - data=body, + response = SubscriberRestTransport._SetIamPolicy._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -2230,8 +2310,9 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) + content = response.content.decode("utf-8") resp = policy_pb2.Policy() - resp = json_format.Parse(response.content.decode("utf-8"), resp) + resp = json_format.Parse(content, resp) resp = self._interceptor.post_set_iam_policy(resp) return resp @@ -2239,7 +2320,35 @@ def __call__( def test_iam_permissions(self): return self._TestIamPermissions(self._session, self._host, self._interceptor) # type: ignore - class _TestIamPermissions(SubscriberRestStub): + class _TestIamPermissions( + _BaseSubscriberRestTransport._BaseTestIamPermissions, SubscriberRestStub + ): + def __hash__(self): + return hash("SubscriberRestTransport.TestIamPermissions") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None, + ): + uri = transcoded_request["uri"] + method = transcoded_request["method"] + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + return response + def __call__( self, request: iam_policy_pb2.TestIamPermissionsRequest, @@ -2263,52 +2372,34 @@ def __call__( iam_policy_pb2.TestIamPermissionsResponse: Response from TestIamPermissions method. """ - http_options: List[Dict[str, str]] = [ - { - "method": "post", - "uri": "/v1/{resource=projects/*/subscriptions/*}:testIamPermissions", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/topics/*}:testIamPermissions", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/snapshots/*}:testIamPermissions", - "body": "*", - }, - { - "method": "post", - "uri": "/v1/{resource=projects/*/schemas/*}:testIamPermissions", - "body": "*", - }, - ] - + http_options = ( + _BaseSubscriberRestTransport._BaseTestIamPermissions._get_http_options() + ) request, metadata = self._interceptor.pre_test_iam_permissions( request, metadata ) - request_kwargs = json_format.MessageToDict(request) - transcoded_request = path_template.transcode(http_options, **request_kwargs) + transcoded_request = _BaseSubscriberRestTransport._BaseTestIamPermissions._get_transcoded_request( + http_options, request + ) - body = json.dumps(transcoded_request["body"]) - uri = transcoded_request["uri"] - method = transcoded_request["method"] + body = _BaseSubscriberRestTransport._BaseTestIamPermissions._get_request_body_json( + transcoded_request + ) # Jsonify the query params - query_params = json.loads(json.dumps(transcoded_request["query_params"])) + query_params = _BaseSubscriberRestTransport._BaseTestIamPermissions._get_query_params_json( + transcoded_request + ) # Send the request - headers = dict(metadata) - headers["Content-Type"] = "application/json" - - response = getattr(self._session, method)( - "{host}{uri}".format(host=self._host, uri=uri), - timeout=timeout, - headers=headers, - params=rest_helpers.flatten_query_params(query_params), - data=body, + response = SubscriberRestTransport._TestIamPermissions._get_response( + self._host, + metadata, + query_params, + self._session, + timeout, + transcoded_request, + body, ) # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception @@ -2316,8 +2407,9 @@ def __call__( if response.status_code >= 400: raise core_exceptions.from_http_response(response) + content = response.content.decode("utf-8") resp = iam_policy_pb2.TestIamPermissionsResponse() - resp = json_format.Parse(response.content.decode("utf-8"), resp) + resp = json_format.Parse(content, resp) resp = self._interceptor.post_test_iam_permissions(resp) return resp diff --git a/google/pubsub_v1/services/subscriber/transports/rest_base.py b/google/pubsub_v1/services/subscriber/transports/rest_base.py new file mode 100644 index 000000000..6626a04c1 --- /dev/null +++ b/google/pubsub_v1/services/subscriber/transports/rest_base.py @@ -0,0 +1,1026 @@ +# -*- coding: utf-8 -*- +# 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. +# 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 json # type: ignore +from google.api_core import path_template +from google.api_core import gapic_v1 + +from google.protobuf import json_format +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from .base import SubscriberTransport, DEFAULT_CLIENT_INFO + +import re +from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union + + +from google.iam.v1 import iam_policy_pb2 # type: ignore +from google.iam.v1 import policy_pb2 # type: ignore +from google.protobuf import empty_pb2 # type: ignore +from google.pubsub_v1.types import pubsub + + +class _BaseSubscriberRestTransport(SubscriberTransport): + """Base REST backend transport for Subscriber. + + Note: This class is not meant to be used directly. Use its sync and + async sub-classes instead. + + This class defines the same methods as the primary client, so the + primary client can load the underlying transport implementation + and call it. + + It sends JSON representations of protocol buffers over HTTP/1.1 + """ + + def __init__( + self, + *, + host: str = "pubsub.googleapis.com", + credentials: Optional[Any] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + always_use_jwt_access: Optional[bool] = False, + url_scheme: str = "https", + api_audience: Optional[str] = None, + ) -> None: + """Instantiate the transport. + Args: + host (Optional[str]): + The hostname to connect to (default: 'pubsub.googleapis.com'). + credentials (Optional[Any]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you are developing + your own client library. + always_use_jwt_access (Optional[bool]): Whether self signed JWT should + be used for service account credentials. + url_scheme: the protocol scheme for the API endpoint. Normally + "https", but for testing or local servers, + "http" can be specified. + """ + # Run the base constructor + maybe_url_match = re.match("^(?Phttp(?:s)?://)?(?P.*)$", host) + if maybe_url_match is None: + raise ValueError( + f"Unexpected hostname structure: {host}" + ) # pragma: NO COVER + + url_match_items = maybe_url_match.groupdict() + + host = f"{url_scheme}://{host}" if not url_match_items["scheme"] else host + + super().__init__( + host=host, + credentials=credentials, + client_info=client_info, + always_use_jwt_access=always_use_jwt_access, + api_audience=api_audience, + ) + + class _BaseAcknowledge: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{subscription=projects/*/subscriptions/*}:acknowledge", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.AcknowledgeRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSubscriberRestTransport._BaseAcknowledge._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseCreateSnapshot: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "put", + "uri": "/v1/{name=projects/*/snapshots/*}", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.CreateSnapshotRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSubscriberRestTransport._BaseCreateSnapshot._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseCreateSubscription: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "put", + "uri": "/v1/{name=projects/*/subscriptions/*}", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.Subscription.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSubscriberRestTransport._BaseCreateSubscription._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseDeleteSnapshot: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "delete", + "uri": "/v1/{snapshot=projects/*/snapshots/*}", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.DeleteSnapshotRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSubscriberRestTransport._BaseDeleteSnapshot._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseDeleteSubscription: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "delete", + "uri": "/v1/{subscription=projects/*/subscriptions/*}", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.DeleteSubscriptionRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSubscriberRestTransport._BaseDeleteSubscription._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseGetSnapshot: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{snapshot=projects/*/snapshots/*}", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.GetSnapshotRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSubscriberRestTransport._BaseGetSnapshot._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseGetSubscription: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{subscription=projects/*/subscriptions/*}", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.GetSubscriptionRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSubscriberRestTransport._BaseGetSubscription._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseListSnapshots: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{project=projects/*}/snapshots", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.ListSnapshotsRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSubscriberRestTransport._BaseListSnapshots._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseListSubscriptions: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{project=projects/*}/subscriptions", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.ListSubscriptionsRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSubscriberRestTransport._BaseListSubscriptions._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseModifyAckDeadline: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{subscription=projects/*/subscriptions/*}:modifyAckDeadline", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.ModifyAckDeadlineRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSubscriberRestTransport._BaseModifyAckDeadline._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseModifyPushConfig: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{subscription=projects/*/subscriptions/*}:modifyPushConfig", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.ModifyPushConfigRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSubscriberRestTransport._BaseModifyPushConfig._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BasePull: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{subscription=projects/*/subscriptions/*}:pull", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.PullRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSubscriberRestTransport._BasePull._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseSeek: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{subscription=projects/*/subscriptions/*}:seek", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.SeekRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSubscriberRestTransport._BaseSeek._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseStreamingPull: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + class _BaseUpdateSnapshot: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "patch", + "uri": "/v1/{snapshot.name=projects/*/snapshots/*}", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.UpdateSnapshotRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSubscriberRestTransport._BaseUpdateSnapshot._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseUpdateSubscription: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "patch", + "uri": "/v1/{subscription.name=projects/*/subscriptions/*}", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + pb_request = pubsub.UpdateSubscriptionRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], use_integers_for_enums=True + ) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + use_integers_for_enums=True, + ) + ) + query_params.update( + _BaseSubscriberRestTransport._BaseUpdateSubscription._get_unset_required_fields( + query_params + ) + ) + + query_params["$alt"] = "json;enum-encoding=int" + return query_params + + class _BaseGetIamPolicy: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{resource=projects/*/topics/*}:getIamPolicy", + }, + { + "method": "get", + "uri": "/v1/{resource=projects/*/subscriptions/*}:getIamPolicy", + }, + { + "method": "get", + "uri": "/v1/{resource=projects/*/snapshots/*}:getIamPolicy", + }, + { + "method": "get", + "uri": "/v1/{resource=projects/*/schemas/*}:getIamPolicy", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + request_kwargs = json_format.MessageToDict(request) + transcoded_request = path_template.transcode(http_options, **request_kwargs) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads(json.dumps(transcoded_request["query_params"])) + return query_params + + class _BaseSetIamPolicy: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{resource=projects/*/topics/*}:setIamPolicy", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/subscriptions/*}:setIamPolicy", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/snapshots/*}:setIamPolicy", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/schemas/*}:setIamPolicy", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + request_kwargs = json_format.MessageToDict(request) + transcoded_request = path_template.transcode(http_options, **request_kwargs) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + body = json.dumps(transcoded_request["body"]) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads(json.dumps(transcoded_request["query_params"])) + return query_params + + class _BaseTestIamPermissions: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{resource=projects/*/subscriptions/*}:testIamPermissions", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/topics/*}:testIamPermissions", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/snapshots/*}:testIamPermissions", + "body": "*", + }, + { + "method": "post", + "uri": "/v1/{resource=projects/*/schemas/*}:testIamPermissions", + "body": "*", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + request_kwargs = json_format.MessageToDict(request) + transcoded_request = path_template.transcode(http_options, **request_kwargs) + return transcoded_request + + @staticmethod + def _get_request_body_json(transcoded_request): + body = json.dumps(transcoded_request["body"]) + return body + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads(json.dumps(transcoded_request["query_params"])) + return query_params + + +__all__ = ("_BaseSubscriberRestTransport",) diff --git a/google/pubsub_v1/types/__init__.py b/google/pubsub_v1/types/__init__.py index 6feefc154..5f2a260b0 100644 --- a/google/pubsub_v1/types/__init__.py +++ b/google/pubsub_v1/types/__init__.py @@ -31,6 +31,8 @@ GetSubscriptionRequest, GetTopicRequest, IngestionDataSourceSettings, + IngestionFailureEvent, + JavaScriptUDF, ListSnapshotsRequest, ListSnapshotsResponse, ListSubscriptionsRequest, @@ -42,6 +44,7 @@ ListTopicSubscriptionsRequest, ListTopicSubscriptionsResponse, MessageStoragePolicy, + MessageTransform, ModifyAckDeadlineRequest, ModifyPushConfigRequest, PlatformLogsSettings, @@ -110,6 +113,8 @@ "GetSubscriptionRequest", "GetTopicRequest", "IngestionDataSourceSettings", + "IngestionFailureEvent", + "JavaScriptUDF", "ListSnapshotsRequest", "ListSnapshotsResponse", "ListSubscriptionsRequest", @@ -121,6 +126,7 @@ "ListTopicSubscriptionsRequest", "ListTopicSubscriptionsResponse", "MessageStoragePolicy", + "MessageTransform", "ModifyAckDeadlineRequest", "ModifyPushConfigRequest", "PlatformLogsSettings", diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index d40d7c24e..5f15e445b 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -32,6 +32,9 @@ "SchemaSettings", "IngestionDataSourceSettings", "PlatformLogsSettings", + "IngestionFailureEvent", + "JavaScriptUDF", + "MessageTransform", "Topic", "PubsubMessage", "GetTopicRequest", @@ -176,6 +179,18 @@ class IngestionDataSourceSettings(proto.Message): cloud_storage (google.pubsub_v1.types.IngestionDataSourceSettings.CloudStorage): Optional. Cloud Storage. + This field is a member of `oneof`_ ``source``. + azure_event_hubs (google.pubsub_v1.types.IngestionDataSourceSettings.AzureEventHubs): + Optional. Azure Event Hubs. + + This field is a member of `oneof`_ ``source``. + aws_msk (google.pubsub_v1.types.IngestionDataSourceSettings.AwsMsk): + Optional. Amazon MSK. + + This field is a member of `oneof`_ ``source``. + confluent_cloud (google.pubsub_v1.types.IngestionDataSourceSettings.ConfluentCloud): + Optional. Confluent Cloud. + This field is a member of `oneof`_ ``source``. platform_logs_settings (google.pubsub_v1.types.PlatformLogsSettings): Optional. Platform Logs settings. If unset, @@ -432,6 +447,268 @@ class PubSubAvroFormat(proto.Message): number=9, ) + class AzureEventHubs(proto.Message): + r"""Ingestion settings for Azure Event Hubs. + + Attributes: + state (google.pubsub_v1.types.IngestionDataSourceSettings.AzureEventHubs.State): + Output only. An output-only field that + indicates the state of the Event Hubs ingestion + source. + resource_group (str): + Optional. Name of the resource group within + the azure subscription. + namespace (str): + Optional. The name of the Event Hubs + namespace. + event_hub (str): + Optional. The name of the Event Hub. + client_id (str): + Optional. The client id of the Azure + application that is being used to authenticate + Pub/Sub. + tenant_id (str): + Optional. The tenant id of the Azure + application that is being used to authenticate + Pub/Sub. + subscription_id (str): + Optional. The Azure subscription id. + gcp_service_account (str): + Optional. The GCP service account to be used + for Federated Identity authentication. + """ + + class State(proto.Enum): + r"""Possible states for managed ingestion from Event Hubs. + + Values: + STATE_UNSPECIFIED (0): + Default value. This value is unused. + ACTIVE (1): + Ingestion is active. + EVENT_HUBS_PERMISSION_DENIED (2): + Permission denied encountered while consuming data from + Event Hubs. This can happen when ``client_id``, or + ``tenant_id`` are invalid. Or the right permissions haven't + been granted. + PUBLISH_PERMISSION_DENIED (3): + Permission denied encountered while + publishing to the topic. + NAMESPACE_NOT_FOUND (4): + The provided Event Hubs namespace couldn't be + found. + EVENT_HUB_NOT_FOUND (5): + The provided Event Hub couldn't be found. + SUBSCRIPTION_NOT_FOUND (6): + The provided Event Hubs subscription couldn't + be found. + RESOURCE_GROUP_NOT_FOUND (7): + The provided Event Hubs resource group + couldn't be found. + """ + STATE_UNSPECIFIED = 0 + ACTIVE = 1 + EVENT_HUBS_PERMISSION_DENIED = 2 + PUBLISH_PERMISSION_DENIED = 3 + NAMESPACE_NOT_FOUND = 4 + EVENT_HUB_NOT_FOUND = 5 + SUBSCRIPTION_NOT_FOUND = 6 + RESOURCE_GROUP_NOT_FOUND = 7 + + state: "IngestionDataSourceSettings.AzureEventHubs.State" = proto.Field( + proto.ENUM, + number=1, + enum="IngestionDataSourceSettings.AzureEventHubs.State", + ) + resource_group: str = proto.Field( + proto.STRING, + number=2, + ) + namespace: str = proto.Field( + proto.STRING, + number=3, + ) + event_hub: str = proto.Field( + proto.STRING, + number=4, + ) + client_id: str = proto.Field( + proto.STRING, + number=5, + ) + tenant_id: str = proto.Field( + proto.STRING, + number=6, + ) + subscription_id: str = proto.Field( + proto.STRING, + number=7, + ) + gcp_service_account: str = proto.Field( + proto.STRING, + number=8, + ) + + class AwsMsk(proto.Message): + r"""Ingestion settings for Amazon MSK. + + Attributes: + state (google.pubsub_v1.types.IngestionDataSourceSettings.AwsMsk.State): + Output only. An output-only field that + indicates the state of the Amazon MSK ingestion + source. + cluster_arn (str): + Required. The Amazon Resource Name (ARN) that + uniquely identifies the cluster. + topic (str): + Required. The name of the topic in the Amazon + MSK cluster that Pub/Sub will import from. + aws_role_arn (str): + Required. AWS role ARN to be used for + Federated Identity authentication with Amazon + MSK. Check the Pub/Sub docs for how to set up + this role and the required permissions that need + to be attached to it. + gcp_service_account (str): + Required. The GCP service account to be used for Federated + Identity authentication with Amazon MSK (via a + ``AssumeRoleWithWebIdentity`` call for the provided role). + The ``aws_role_arn`` must be set up with + ``accounts.google.com:sub`` equals to this service account + number. + """ + + class State(proto.Enum): + r"""Possible states for managed ingestion from Amazon MSK. + + Values: + STATE_UNSPECIFIED (0): + Default value. This value is unused. + ACTIVE (1): + Ingestion is active. + MSK_PERMISSION_DENIED (2): + Permission denied encountered while consuming + data from Amazon MSK. + PUBLISH_PERMISSION_DENIED (3): + Permission denied encountered while + publishing to the topic. + CLUSTER_NOT_FOUND (4): + The provided MSK cluster wasn't found. + TOPIC_NOT_FOUND (5): + The provided topic wasn't found. + """ + STATE_UNSPECIFIED = 0 + ACTIVE = 1 + MSK_PERMISSION_DENIED = 2 + PUBLISH_PERMISSION_DENIED = 3 + CLUSTER_NOT_FOUND = 4 + TOPIC_NOT_FOUND = 5 + + state: "IngestionDataSourceSettings.AwsMsk.State" = proto.Field( + proto.ENUM, + number=1, + enum="IngestionDataSourceSettings.AwsMsk.State", + ) + cluster_arn: str = proto.Field( + proto.STRING, + number=2, + ) + topic: str = proto.Field( + proto.STRING, + number=3, + ) + aws_role_arn: str = proto.Field( + proto.STRING, + number=4, + ) + gcp_service_account: str = proto.Field( + proto.STRING, + number=5, + ) + + class ConfluentCloud(proto.Message): + r"""Ingestion settings for Confluent Cloud. + + Attributes: + state (google.pubsub_v1.types.IngestionDataSourceSettings.ConfluentCloud.State): + Output only. An output-only field that + indicates the state of the Confluent Cloud + ingestion source. + bootstrap_server (str): + Required. The address of the bootstrap + server. The format is url:port. + cluster_id (str): + Required. The id of the cluster. + topic (str): + Required. The name of the topic in the + Confluent Cloud cluster that Pub/Sub will import + from. + identity_pool_id (str): + Required. The id of the identity pool to be + used for Federated Identity authentication with + Confluent Cloud. See + https://docs.confluent.io/cloud/current/security/authenticate/workload-identities/identity-providers/oauth/identity-pools.html#add-oauth-identity-pools. + gcp_service_account (str): + Required. The GCP service account to be used for Federated + Identity authentication with ``identity_pool_id``. + """ + + class State(proto.Enum): + r"""Possible states for managed ingestion from Confluent Cloud. + + Values: + STATE_UNSPECIFIED (0): + Default value. This value is unused. + ACTIVE (1): + Ingestion is active. + CONFLUENT_CLOUD_PERMISSION_DENIED (2): + Permission denied encountered while consuming + data from Confluent Cloud. + PUBLISH_PERMISSION_DENIED (3): + Permission denied encountered while + publishing to the topic. + UNREACHABLE_BOOTSTRAP_SERVER (4): + The provided bootstrap server address is + unreachable. + CLUSTER_NOT_FOUND (5): + The provided cluster wasn't found. + TOPIC_NOT_FOUND (6): + The provided topic wasn't found. + """ + STATE_UNSPECIFIED = 0 + ACTIVE = 1 + CONFLUENT_CLOUD_PERMISSION_DENIED = 2 + PUBLISH_PERMISSION_DENIED = 3 + UNREACHABLE_BOOTSTRAP_SERVER = 4 + CLUSTER_NOT_FOUND = 5 + TOPIC_NOT_FOUND = 6 + + state: "IngestionDataSourceSettings.ConfluentCloud.State" = proto.Field( + proto.ENUM, + number=1, + enum="IngestionDataSourceSettings.ConfluentCloud.State", + ) + bootstrap_server: str = proto.Field( + proto.STRING, + number=2, + ) + cluster_id: str = proto.Field( + proto.STRING, + number=3, + ) + topic: str = proto.Field( + proto.STRING, + number=4, + ) + identity_pool_id: str = proto.Field( + proto.STRING, + number=5, + ) + gcp_service_account: str = proto.Field( + proto.STRING, + number=6, + ) + aws_kinesis: AwsKinesis = proto.Field( proto.MESSAGE, number=1, @@ -444,6 +721,24 @@ class PubSubAvroFormat(proto.Message): oneof="source", message=CloudStorage, ) + azure_event_hubs: AzureEventHubs = proto.Field( + proto.MESSAGE, + number=3, + oneof="source", + message=AzureEventHubs, + ) + aws_msk: AwsMsk = proto.Field( + proto.MESSAGE, + number=5, + oneof="source", + message=AwsMsk, + ) + confluent_cloud: ConfluentCloud = proto.Field( + proto.MESSAGE, + number=6, + oneof="source", + message=ConfluentCloud, + ) platform_logs_settings: "PlatformLogsSettings" = proto.Field( proto.MESSAGE, number=4, @@ -495,6 +790,386 @@ class Severity(proto.Enum): ) +class IngestionFailureEvent(proto.Message): + r"""Payload of the Platform Log entry sent when a failure is + encountered while ingesting. + + This message has `oneof`_ fields (mutually exclusive fields). + For each oneof, at most one member field can be set at the same time. + Setting any member of the oneof automatically clears all other + members. + + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields + + Attributes: + topic (str): + Required. Name of the import topic. Format is: + projects/{project_name}/topics/{topic_name}. + error_message (str): + Required. Error details explaining why + ingestion to Pub/Sub has failed. + cloud_storage_failure (google.pubsub_v1.types.IngestionFailureEvent.CloudStorageFailure): + Optional. Failure when ingesting from Cloud + Storage. + + This field is a member of `oneof`_ ``failure``. + aws_msk_failure (google.pubsub_v1.types.IngestionFailureEvent.AwsMskFailureReason): + Optional. Failure when ingesting from Amazon + MSK. + + This field is a member of `oneof`_ ``failure``. + azure_event_hubs_failure (google.pubsub_v1.types.IngestionFailureEvent.AzureEventHubsFailureReason): + Optional. Failure when ingesting from Azure + Event Hubs. + + This field is a member of `oneof`_ ``failure``. + confluent_cloud_failure (google.pubsub_v1.types.IngestionFailureEvent.ConfluentCloudFailureReason): + Optional. Failure when ingesting from + Confluent Cloud. + + This field is a member of `oneof`_ ``failure``. + """ + + class ApiViolationReason(proto.Message): + r"""Specifies the reason why some data may have been left out of the + desired Pub/Sub message due to the API message limits + (https://cloud.google.com/pubsub/quotas#resource_limits). For + example, when the number of attributes is larger than 100, the + number of attributes is truncated to 100 to respect the limit on the + attribute count. Other attribute limits are treated similarly. When + the size of the desired message would've been larger than 10MB, the + message won't be published at all, and ingestion of the subsequent + messages will proceed as normal. + + """ + + class AvroFailureReason(proto.Message): + r"""Set when an Avro file is unsupported or its format is not + valid. When this occurs, one or more Avro objects won't be + ingested. + + """ + + class CloudStorageFailure(proto.Message): + r"""Failure when ingesting from a Cloud Storage source. + + This message has `oneof`_ fields (mutually exclusive fields). + For each oneof, at most one member field can be set at the same time. + Setting any member of the oneof automatically clears all other + members. + + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields + + Attributes: + bucket (str): + Optional. Name of the Cloud Storage bucket + used for ingestion. + object_name (str): + Optional. Name of the Cloud Storage object + which contained the section that couldn't be + ingested. + object_generation (int): + Optional. Generation of the Cloud Storage + object which contained the section that couldn't + be ingested. + avro_failure_reason (google.pubsub_v1.types.IngestionFailureEvent.AvroFailureReason): + Optional. Failure encountered when parsing an + Avro file. + + This field is a member of `oneof`_ ``reason``. + api_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.ApiViolationReason): + Optional. The Pub/Sub API limits prevented + the desired message from being published. + + This field is a member of `oneof`_ ``reason``. + """ + + bucket: str = proto.Field( + proto.STRING, + number=1, + ) + object_name: str = proto.Field( + proto.STRING, + number=2, + ) + object_generation: int = proto.Field( + proto.INT64, + number=3, + ) + avro_failure_reason: "IngestionFailureEvent.AvroFailureReason" = proto.Field( + proto.MESSAGE, + number=5, + oneof="reason", + message="IngestionFailureEvent.AvroFailureReason", + ) + api_violation_reason: "IngestionFailureEvent.ApiViolationReason" = proto.Field( + proto.MESSAGE, + number=6, + oneof="reason", + message="IngestionFailureEvent.ApiViolationReason", + ) + + class AwsMskFailureReason(proto.Message): + r"""Failure when ingesting from an Amazon MSK source. + + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields + + Attributes: + cluster_arn (str): + Optional. The ARN of the cluster of the topic + being ingested from. + kafka_topic (str): + Optional. The name of the Kafka topic being + ingested from. + partition_id (int): + Optional. The partition ID of the message + that failed to be ingested. + offset (int): + Optional. The offset within the partition of + the message that failed to be ingested. + api_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.ApiViolationReason): + Optional. The Pub/Sub API limits prevented + the desired message from being published. + + This field is a member of `oneof`_ ``reason``. + """ + + cluster_arn: str = proto.Field( + proto.STRING, + number=1, + ) + kafka_topic: str = proto.Field( + proto.STRING, + number=2, + ) + partition_id: int = proto.Field( + proto.INT64, + number=3, + ) + offset: int = proto.Field( + proto.INT64, + number=4, + ) + api_violation_reason: "IngestionFailureEvent.ApiViolationReason" = proto.Field( + proto.MESSAGE, + number=5, + oneof="reason", + message="IngestionFailureEvent.ApiViolationReason", + ) + + class AzureEventHubsFailureReason(proto.Message): + r"""Failure when ingesting from an Azure Event Hubs source. + + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields + + Attributes: + namespace (str): + Optional. The namespace containing the event + hub being ingested from. + event_hub (str): + Optional. The name of the event hub being + ingested from. + partition_id (int): + Optional. The partition ID of the message + that failed to be ingested. + offset (int): + Optional. The offset within the partition of + the message that failed to be ingested. + api_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.ApiViolationReason): + Optional. The Pub/Sub API limits prevented + the desired message from being published. + + This field is a member of `oneof`_ ``reason``. + """ + + namespace: str = proto.Field( + proto.STRING, + number=1, + ) + event_hub: str = proto.Field( + proto.STRING, + number=2, + ) + partition_id: int = proto.Field( + proto.INT64, + number=3, + ) + offset: int = proto.Field( + proto.INT64, + number=4, + ) + api_violation_reason: "IngestionFailureEvent.ApiViolationReason" = proto.Field( + proto.MESSAGE, + number=5, + oneof="reason", + message="IngestionFailureEvent.ApiViolationReason", + ) + + class ConfluentCloudFailureReason(proto.Message): + r"""Failure when ingesting from a Confluent Cloud source. + + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields + + Attributes: + cluster_id (str): + Optional. The cluster ID containing the topic + being ingested from. + kafka_topic (str): + Optional. The name of the Kafka topic being + ingested from. + partition_id (int): + Optional. The partition ID of the message + that failed to be ingested. + offset (int): + Optional. The offset within the partition of + the message that failed to be ingested. + api_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.ApiViolationReason): + Optional. The Pub/Sub API limits prevented + the desired message from being published. + + This field is a member of `oneof`_ ``reason``. + """ + + cluster_id: str = proto.Field( + proto.STRING, + number=1, + ) + kafka_topic: str = proto.Field( + proto.STRING, + number=2, + ) + partition_id: int = proto.Field( + proto.INT64, + number=3, + ) + offset: int = proto.Field( + proto.INT64, + number=4, + ) + api_violation_reason: "IngestionFailureEvent.ApiViolationReason" = proto.Field( + proto.MESSAGE, + number=5, + oneof="reason", + message="IngestionFailureEvent.ApiViolationReason", + ) + + topic: str = proto.Field( + proto.STRING, + number=1, + ) + error_message: str = proto.Field( + proto.STRING, + number=2, + ) + cloud_storage_failure: CloudStorageFailure = proto.Field( + proto.MESSAGE, + number=3, + oneof="failure", + message=CloudStorageFailure, + ) + aws_msk_failure: AwsMskFailureReason = proto.Field( + proto.MESSAGE, + number=4, + oneof="failure", + message=AwsMskFailureReason, + ) + azure_event_hubs_failure: AzureEventHubsFailureReason = proto.Field( + proto.MESSAGE, + number=5, + oneof="failure", + message=AzureEventHubsFailureReason, + ) + confluent_cloud_failure: ConfluentCloudFailureReason = proto.Field( + proto.MESSAGE, + number=6, + oneof="failure", + message=ConfluentCloudFailureReason, + ) + + +class JavaScriptUDF(proto.Message): + r"""User-defined JavaScript function that can transform or filter + a Pub/Sub message. + + Attributes: + function_name (str): + Required. Name of the JavasScript function + that should applied to Pub/Sub messages. + code (str): + Required. JavaScript code that contains a function + ``function_name`` with the below signature: + + + :: + + // /** + // * Transforms a Pub/Sub message. + // + // * @return {(Object)>|null)} - To + // * filter a message, return `null`. To transform a message return a map + // * with the following keys: + // * - (required) 'data' : {string} + // * - (optional) 'attributes' : {Object} + // * Returning empty `attributes` will remove all attributes from the + // * message. + // * + // * @param {(Object)>} Pub/Sub + // * message. Keys: + // * - (required) 'data' : {string} + // * - (required) 'attributes' : {Object} + // * + // * @param {Object} metadata - Pub/Sub message metadata. + // * Keys: + // * - (required) 'message_id' : {string} + // * - (optional) 'publish_time': {string} YYYY-MM-DDTHH:MM:SSZ format + // * - (optional) 'ordering_key': {string} + // */ + // + // function (message, metadata) { + // } + + """ + + function_name: str = proto.Field( + proto.STRING, + number=1, + ) + code: str = proto.Field( + proto.STRING, + number=2, + ) + + +class MessageTransform(proto.Message): + r"""All supported message transforms types. + + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields + + Attributes: + javascript_udf (google.pubsub_v1.types.JavaScriptUDF): + Optional. JavaScript User Defined Function. If multiple + JavaScriptUDF's are specified on a resource, each must have + a unique ``function_name``. + + This field is a member of `oneof`_ ``transform``. + enabled (bool): + Optional. If set to true, the transform is enabled. If + false, the transform is disabled and will not be applied to + messages. Defaults to ``true``. + """ + + javascript_udf: "JavaScriptUDF" = proto.Field( + proto.MESSAGE, + number=2, + oneof="transform", + message="JavaScriptUDF", + ) + enabled: bool = proto.Field( + proto.BOOL, + number=3, + ) + + class Topic(proto.Message): r"""A topic resource. @@ -547,6 +1222,10 @@ class Topic(proto.Message): ingestion_data_source_settings (google.pubsub_v1.types.IngestionDataSourceSettings): Optional. Settings for ingestion from a data source into this topic. + message_transforms (MutableSequence[google.pubsub_v1.types.MessageTransform]): + Optional. Transforms to be applied to + messages published to the topic. Transforms are + applied in the order specified. """ class State(proto.Enum): @@ -610,6 +1289,11 @@ class State(proto.Enum): number=10, message="IngestionDataSourceSettings", ) + message_transforms: MutableSequence["MessageTransform"] = proto.RepeatedField( + proto.MESSAGE, + number=13, + message="MessageTransform", + ) class PubsubMessage(proto.Message): @@ -1138,6 +1822,11 @@ class Subscription(proto.Message): Output only. Information about the associated Analytics Hub subscription. Only set if the subscritpion is created by Analytics Hub. + message_transforms (MutableSequence[google.pubsub_v1.types.MessageTransform]): + Optional. Transforms to be applied to + messages before they are delivered to + subscribers. Transforms are applied in the order + specified. """ class State(proto.Enum): @@ -1160,8 +1849,8 @@ class State(proto.Enum): RESOURCE_ERROR = 2 class AnalyticsHubSubscriptionInfo(proto.Message): - r"""Information about an associated Analytics Hub subscription - (https://cloud.google.com/bigquery/docs/analytics-hub-manage-subscriptions). + r"""Information about an associated `Analytics Hub + subscription `__. Attributes: listing (str): @@ -1271,6 +1960,11 @@ class AnalyticsHubSubscriptionInfo(proto.Message): number=23, message=AnalyticsHubSubscriptionInfo, ) + message_transforms: MutableSequence["MessageTransform"] = proto.RepeatedField( + proto.MESSAGE, + number=25, + message="MessageTransform", + ) class RetryPolicy(proto.Message): diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index f06040919..d66015ac4 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.27.3" + "version": "0.1.0" }, "snippets": [ { diff --git a/scripts/fixup_pubsub_v1_keywords.py b/scripts/fixup_pubsub_v1_keywords.py index 543f7e051..afcf7a8b9 100644 --- a/scripts/fixup_pubsub_v1_keywords.py +++ b/scripts/fixup_pubsub_v1_keywords.py @@ -43,8 +43,8 @@ class pubsubCallTransformer(cst.CSTTransformer): 'commit_schema': ('name', 'schema', ), 'create_schema': ('parent', 'schema', 'schema_id', ), 'create_snapshot': ('name', 'subscription', 'labels', ), - 'create_subscription': ('name', 'topic', 'push_config', 'bigquery_config', 'cloud_storage_config', 'ack_deadline_seconds', 'retain_acked_messages', 'message_retention_duration', 'labels', 'enable_message_ordering', 'expiration_policy', 'filter', 'dead_letter_policy', 'retry_policy', 'detached', 'enable_exactly_once_delivery', 'topic_message_retention_duration', 'state', 'analytics_hub_subscription_info', ), - 'create_topic': ('name', 'labels', 'message_storage_policy', 'kms_key_name', 'schema_settings', 'satisfies_pzs', 'message_retention_duration', 'state', 'ingestion_data_source_settings', ), + 'create_subscription': ('name', 'topic', 'push_config', 'bigquery_config', 'cloud_storage_config', 'ack_deadline_seconds', 'retain_acked_messages', 'message_retention_duration', 'labels', 'enable_message_ordering', 'expiration_policy', 'filter', 'dead_letter_policy', 'retry_policy', 'detached', 'enable_exactly_once_delivery', 'topic_message_retention_duration', 'state', 'analytics_hub_subscription_info', 'message_transforms', ), + 'create_topic': ('name', 'labels', 'message_storage_policy', 'kms_key_name', 'schema_settings', 'satisfies_pzs', 'message_retention_duration', 'state', 'ingestion_data_source_settings', 'message_transforms', ), 'delete_schema': ('name', ), 'delete_schema_revision': ('name', 'revision_id', ), 'delete_snapshot': ('snapshot', ), diff --git a/tests/unit/gapic/pubsub_v1/test_publisher.py b/tests/unit/gapic/pubsub_v1/test_publisher.py index 5af39dbf8..423df4433 100644 --- a/tests/unit/gapic/pubsub_v1/test_publisher.py +++ b/tests/unit/gapic/pubsub_v1/test_publisher.py @@ -24,7 +24,7 @@ import grpc from grpc.experimental import aio -from collections.abc import Iterable +from collections.abc import Iterable, AsyncIterable from google.protobuf import json_format import json import math @@ -37,6 +37,13 @@ from requests.sessions import Session from google.protobuf import json_format +try: + from google.auth.aio import credentials as ga_credentials_async + + HAS_GOOGLE_AUTH_AIO = True +except ImportError: # pragma: NO COVER + HAS_GOOGLE_AUTH_AIO = False + from google.api_core import client_options from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 @@ -62,10 +69,24 @@ import google.auth +async def mock_async_gen(data, chunk_size=1): + for i in range(0, len(data)): # pragma: NO COVER + chunk = data[i : i + chunk_size] + yield chunk.encode("utf-8") + + def client_cert_source_callback(): return b"cert bytes", b"key bytes" +# TODO: use async auth anon credentials by default once the minimum version of google-auth is upgraded. +# See related issue: https://github.com/googleapis/gapic-generator-python/issues/2107. +def async_anonymous_credentials(): + if HAS_GOOGLE_AUTH_AIO: + return ga_credentials_async.AnonymousCredentials() + return ga_credentials.AnonymousCredentials() + + # If default endpoint is localhost, then default mtls endpoint will be the same. # This method modifies the default endpoint so the client can produce a different # mtls endpoint for endpoint testing purposes. @@ -280,86 +301,6 @@ def test__get_universe_domain(): assert str(excinfo.value) == "Universe Domain cannot be an empty string." -@pytest.mark.parametrize( - "client_class,transport_class,transport_name", - [ - (PublisherClient, transports.PublisherGrpcTransport, "grpc"), - (PublisherClient, transports.PublisherRestTransport, "rest"), - ], -) -def test__validate_universe_domain(client_class, transport_class, transport_name): - client = client_class( - transport=transport_class(credentials=ga_credentials.AnonymousCredentials()) - ) - assert client._validate_universe_domain() == True - - # Test the case when universe is already validated. - assert client._validate_universe_domain() == True - - if transport_name == "grpc": - # Test the case where credentials are provided by the - # `local_channel_credentials`. The default universes in both match. - channel = grpc.secure_channel( - "http://localhost/", grpc.local_channel_credentials() - ) - client = client_class(transport=transport_class(channel=channel)) - assert client._validate_universe_domain() == True - - # Test the case where credentials do not exist: e.g. a transport is provided - # with no credentials. Validation should still succeed because there is no - # mismatch with non-existent credentials. - channel = grpc.secure_channel( - "http://localhost/", grpc.local_channel_credentials() - ) - transport = transport_class(channel=channel) - transport._credentials = None - client = client_class(transport=transport) - assert client._validate_universe_domain() == True - - # TODO: This is needed to cater for older versions of google-auth - # Make this test unconditional once the minimum supported version of - # google-auth becomes 2.23.0 or higher. - google_auth_major, google_auth_minor = [ - int(part) for part in google.auth.__version__.split(".")[0:2] - ] - if google_auth_major > 2 or (google_auth_major == 2 and google_auth_minor >= 23): - credentials = ga_credentials.AnonymousCredentials() - credentials._universe_domain = "foo.com" - # Test the case when there is a universe mismatch from the credentials. - client = client_class(transport=transport_class(credentials=credentials)) - with pytest.raises(ValueError) as excinfo: - client._validate_universe_domain() - assert ( - str(excinfo.value) - == "The configured universe domain (googleapis.com) does not match the universe domain found in the credentials (foo.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default." - ) - - # Test the case when there is a universe mismatch from the client. - # - # TODO: Make this test unconditional once the minimum supported version of - # google-api-core becomes 2.15.0 or higher. - api_core_major, api_core_minor = [ - int(part) for part in api_core_version.__version__.split(".")[0:2] - ] - if api_core_major > 2 or (api_core_major == 2 and api_core_minor >= 15): - client = client_class( - client_options={"universe_domain": "bar.com"}, - transport=transport_class( - credentials=ga_credentials.AnonymousCredentials(), - ), - ) - with pytest.raises(ValueError) as excinfo: - client._validate_universe_domain() - assert ( - str(excinfo.value) - == "The configured universe domain (bar.com) does not match the universe domain found in the credentials (googleapis.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default." - ) - - # Test that ValueError is raised if universe_domain is provided via client options and credentials is None - with pytest.raises(ValueError): - client._compare_universes("foo.bar", None) - - @pytest.mark.parametrize( "client_class,transport_name", [ @@ -1129,25 +1070,6 @@ def test_create_topic(request_type, transport: str = "grpc"): assert response.state == pubsub.Topic.State.ACTIVE -def test_create_topic_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.create_topic), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.create_topic() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.Topic() - - def test_create_topic_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -1213,32 +1135,6 @@ def test_create_topic_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_create_topic_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.create_topic), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.Topic( - name="name_value", - kms_key_name="kms_key_name_value", - satisfies_pzs=True, - state=pubsub.Topic.State.ACTIVE, - ) - ) - response = await client.create_topic() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.Topic() - - @pytest.mark.asyncio async def test_create_topic_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -1247,7 +1143,7 @@ async def test_create_topic_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -1286,7 +1182,7 @@ async def test_create_topic_async( transport: str = "grpc_asyncio", request_type=pubsub.Topic ): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -1358,7 +1254,7 @@ def test_create_topic_field_headers(): @pytest.mark.asyncio async def test_create_topic_field_headers_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -1426,7 +1322,7 @@ def test_create_topic_flattened_error(): @pytest.mark.asyncio async def test_create_topic_flattened_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -1453,7 +1349,7 @@ async def test_create_topic_flattened_async(): @pytest.mark.asyncio async def test_create_topic_flattened_error_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -1507,25 +1403,6 @@ def test_update_topic(request_type, transport: str = "grpc"): assert response.state == pubsub.Topic.State.ACTIVE -def test_update_topic_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.update_topic), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.update_topic() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.UpdateTopicRequest() - - def test_update_topic_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -1585,32 +1462,6 @@ def test_update_topic_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_update_topic_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.update_topic), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.Topic( - name="name_value", - kms_key_name="kms_key_name_value", - satisfies_pzs=True, - state=pubsub.Topic.State.ACTIVE, - ) - ) - response = await client.update_topic() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.UpdateTopicRequest() - - @pytest.mark.asyncio async def test_update_topic_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -1619,7 +1470,7 @@ async def test_update_topic_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -1658,7 +1509,7 @@ async def test_update_topic_async( transport: str = "grpc_asyncio", request_type=pubsub.UpdateTopicRequest ): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -1730,7 +1581,7 @@ def test_update_topic_field_headers(): @pytest.mark.asyncio async def test_update_topic_field_headers_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -1803,7 +1654,7 @@ def test_update_topic_flattened_error(): @pytest.mark.asyncio async def test_update_topic_flattened_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -1834,7 +1685,7 @@ async def test_update_topic_flattened_async(): @pytest.mark.asyncio async def test_update_topic_flattened_error_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -1883,25 +1734,6 @@ def test_publish(request_type, transport: str = "grpc"): assert response.message_ids == ["message_ids_value"] -def test_publish_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.publish), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.publish() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.PublishRequest() - - def test_publish_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -1965,36 +1797,13 @@ def test_publish_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_publish_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.publish), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.PublishResponse( - message_ids=["message_ids_value"], - ) - ) - response = await client.publish() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.PublishRequest() - - @pytest.mark.asyncio async def test_publish_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio"): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -2033,7 +1842,7 @@ async def test_publish_async( transport: str = "grpc_asyncio", request_type=pubsub.PublishRequest ): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -2099,7 +1908,7 @@ def test_publish_field_headers(): @pytest.mark.asyncio async def test_publish_field_headers_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -2174,7 +1983,7 @@ def test_publish_flattened_error(): @pytest.mark.asyncio async def test_publish_flattened_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2207,7 +2016,7 @@ async def test_publish_flattened_async(): @pytest.mark.asyncio async def test_publish_flattened_error_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -2262,25 +2071,6 @@ def test_get_topic(request_type, transport: str = "grpc"): assert response.state == pubsub.Topic.State.ACTIVE -def test_get_topic_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.get_topic), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.get_topic() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.GetTopicRequest() - - def test_get_topic_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -2344,39 +2134,13 @@ def test_get_topic_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_get_topic_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.get_topic), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.Topic( - name="name_value", - kms_key_name="kms_key_name_value", - satisfies_pzs=True, - state=pubsub.Topic.State.ACTIVE, - ) - ) - response = await client.get_topic() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.GetTopicRequest() - - @pytest.mark.asyncio async def test_get_topic_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio"): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -2415,7 +2179,7 @@ async def test_get_topic_async( transport: str = "grpc_asyncio", request_type=pubsub.GetTopicRequest ): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -2487,7 +2251,7 @@ def test_get_topic_field_headers(): @pytest.mark.asyncio async def test_get_topic_field_headers_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -2555,7 +2319,7 @@ def test_get_topic_flattened_error(): @pytest.mark.asyncio async def test_get_topic_flattened_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2582,7 +2346,7 @@ async def test_get_topic_flattened_async(): @pytest.mark.asyncio async def test_get_topic_flattened_error_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -2630,25 +2394,6 @@ def test_list_topics(request_type, transport: str = "grpc"): assert response.next_page_token == "next_page_token_value" -def test_list_topics_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.list_topics), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.list_topics() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListTopicsRequest() - - def test_list_topics_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -2714,29 +2459,6 @@ def test_list_topics_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_list_topics_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.list_topics), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.ListTopicsResponse( - next_page_token="next_page_token_value", - ) - ) - response = await client.list_topics() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListTopicsRequest() - - @pytest.mark.asyncio async def test_list_topics_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -2745,7 +2467,7 @@ async def test_list_topics_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -2784,7 +2506,7 @@ async def test_list_topics_async( transport: str = "grpc_asyncio", request_type=pubsub.ListTopicsRequest ): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -2850,7 +2572,7 @@ def test_list_topics_field_headers(): @pytest.mark.asyncio async def test_list_topics_field_headers_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -2920,7 +2642,7 @@ def test_list_topics_flattened_error(): @pytest.mark.asyncio async def test_list_topics_flattened_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2949,7 +2671,7 @@ async def test_list_topics_flattened_async(): @pytest.mark.asyncio async def test_list_topics_flattened_error_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -3059,7 +2781,7 @@ def test_list_topics_pages(transport_name: str = "grpc"): @pytest.mark.asyncio async def test_list_topics_async_pager(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -3109,7 +2831,7 @@ async def test_list_topics_async_pager(): @pytest.mark.asyncio async def test_list_topics_async_pages(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -3195,27 +2917,6 @@ def test_list_topic_subscriptions(request_type, transport: str = "grpc"): assert response.next_page_token == "next_page_token_value" -def test_list_topic_subscriptions_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.list_topic_subscriptions), "__call__" - ) as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.list_topic_subscriptions() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListTopicSubscriptionsRequest() - - def test_list_topic_subscriptions_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -3288,32 +2989,6 @@ def test_list_topic_subscriptions_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_list_topic_subscriptions_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.list_topic_subscriptions), "__call__" - ) as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.ListTopicSubscriptionsResponse( - subscriptions=["subscriptions_value"], - next_page_token="next_page_token_value", - ) - ) - response = await client.list_topic_subscriptions() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListTopicSubscriptionsRequest() - - @pytest.mark.asyncio async def test_list_topic_subscriptions_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -3322,7 +2997,7 @@ async def test_list_topic_subscriptions_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -3361,7 +3036,7 @@ async def test_list_topic_subscriptions_async( transport: str = "grpc_asyncio", request_type=pubsub.ListTopicSubscriptionsRequest ): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -3433,7 +3108,7 @@ def test_list_topic_subscriptions_field_headers(): @pytest.mark.asyncio async def test_list_topic_subscriptions_field_headers_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -3507,7 +3182,7 @@ def test_list_topic_subscriptions_flattened_error(): @pytest.mark.asyncio async def test_list_topic_subscriptions_flattened_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -3538,7 +3213,7 @@ async def test_list_topic_subscriptions_flattened_async(): @pytest.mark.asyncio async def test_list_topic_subscriptions_flattened_error_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -3654,7 +3329,7 @@ def test_list_topic_subscriptions_pages(transport_name: str = "grpc"): @pytest.mark.asyncio async def test_list_topic_subscriptions_async_pager(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -3706,7 +3381,7 @@ async def test_list_topic_subscriptions_async_pager(): @pytest.mark.asyncio async def test_list_topic_subscriptions_async_pages(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -3794,27 +3469,6 @@ def test_list_topic_snapshots(request_type, transport: str = "grpc"): assert response.next_page_token == "next_page_token_value" -def test_list_topic_snapshots_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.list_topic_snapshots), "__call__" - ) as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.list_topic_snapshots() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListTopicSnapshotsRequest() - - def test_list_topic_snapshots_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -3886,32 +3540,6 @@ def test_list_topic_snapshots_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_list_topic_snapshots_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.list_topic_snapshots), "__call__" - ) as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.ListTopicSnapshotsResponse( - snapshots=["snapshots_value"], - next_page_token="next_page_token_value", - ) - ) - response = await client.list_topic_snapshots() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListTopicSnapshotsRequest() - - @pytest.mark.asyncio async def test_list_topic_snapshots_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -3920,7 +3548,7 @@ async def test_list_topic_snapshots_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -3959,7 +3587,7 @@ async def test_list_topic_snapshots_async( transport: str = "grpc_asyncio", request_type=pubsub.ListTopicSnapshotsRequest ): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4031,7 +3659,7 @@ def test_list_topic_snapshots_field_headers(): @pytest.mark.asyncio async def test_list_topic_snapshots_field_headers_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -4105,7 +3733,7 @@ def test_list_topic_snapshots_flattened_error(): @pytest.mark.asyncio async def test_list_topic_snapshots_flattened_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -4136,7 +3764,7 @@ async def test_list_topic_snapshots_flattened_async(): @pytest.mark.asyncio async def test_list_topic_snapshots_flattened_error_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -4250,7 +3878,7 @@ def test_list_topic_snapshots_pages(transport_name: str = "grpc"): @pytest.mark.asyncio async def test_list_topic_snapshots_async_pager(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -4302,7 +3930,7 @@ async def test_list_topic_snapshots_async_pager(): @pytest.mark.asyncio async def test_list_topic_snapshots_async_pages(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -4383,25 +4011,6 @@ def test_delete_topic(request_type, transport: str = "grpc"): assert response is None -def test_delete_topic_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.delete_topic), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.delete_topic() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DeleteTopicRequest() - - def test_delete_topic_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -4465,25 +4074,6 @@ def test_delete_topic_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_delete_topic_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.delete_topic), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) - response = await client.delete_topic() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DeleteTopicRequest() - - @pytest.mark.asyncio async def test_delete_topic_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -4492,7 +4082,7 @@ async def test_delete_topic_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4531,7 +4121,7 @@ async def test_delete_topic_async( transport: str = "grpc_asyncio", request_type=pubsub.DeleteTopicRequest ): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4592,7 +4182,7 @@ def test_delete_topic_field_headers(): @pytest.mark.asyncio async def test_delete_topic_field_headers_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -4660,7 +4250,7 @@ def test_delete_topic_flattened_error(): @pytest.mark.asyncio async def test_delete_topic_flattened_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -4687,7 +4277,7 @@ async def test_delete_topic_flattened_async(): @pytest.mark.asyncio async def test_delete_topic_flattened_error_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -4734,27 +4324,6 @@ def test_detach_subscription(request_type, transport: str = "grpc"): assert isinstance(response, pubsub.DetachSubscriptionResponse) -def test_detach_subscription_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.detach_subscription), "__call__" - ) as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.detach_subscription() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DetachSubscriptionRequest() - - def test_detach_subscription_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -4824,29 +4393,6 @@ def test_detach_subscription_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_detach_subscription_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.detach_subscription), "__call__" - ) as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.DetachSubscriptionResponse() - ) - response = await client.detach_subscription() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DetachSubscriptionRequest() - - @pytest.mark.asyncio async def test_detach_subscription_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -4855,7 +4401,7 @@ async def test_detach_subscription_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4894,7 +4440,7 @@ async def test_detach_subscription_async( transport: str = "grpc_asyncio", request_type=pubsub.DetachSubscriptionRequest ): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4961,7 +4507,7 @@ def test_detach_subscription_field_headers(): @pytest.mark.asyncio async def test_detach_subscription_field_headers_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -4992,52 +4538,6 @@ async def test_detach_subscription_field_headers_async(): ) in kw["metadata"] -@pytest.mark.parametrize( - "request_type", - [ - pubsub.Topic, - dict, - ], -) -def test_create_topic_rest(request_type): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.Topic( - name="name_value", - kms_key_name="kms_key_name_value", - satisfies_pzs=True, - state=pubsub.Topic.State.ACTIVE, - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.Topic.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.create_topic(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pubsub.Topic) - assert response.name == "name_value" - assert response.kms_key_name == "kms_key_name_value" - assert response.satisfies_pzs is True - assert response.state == pubsub.Topic.State.ACTIVE - - def test_create_topic_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -5156,81 +4656,6 @@ def test_create_topic_rest_unset_required_fields(): assert set(unset_fields) == (set(()) & set(("name",))) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_create_topic_rest_interceptors(null_interceptor): - transport = transports.PublisherRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), - ) - client = PublisherClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.PublisherRestInterceptor, "post_create_topic" - ) as post, mock.patch.object( - transports.PublisherRestInterceptor, "pre_create_topic" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.Topic.pb(pubsub.Topic()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.Topic.to_json(pubsub.Topic()) - - request = pubsub.Topic() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.Topic() - - client.create_topic( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_create_topic_rest_bad_request( - transport: str = "rest", request_type=pubsub.Topic -): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.create_topic(request) - - def test_create_topic_rest_flattened(): client = PublisherClient( credentials=ga_credentials.AnonymousCredentials(), @@ -5286,58 +4711,6 @@ def test_create_topic_rest_flattened_error(transport: str = "rest"): ) -def test_create_topic_rest_error(): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.UpdateTopicRequest, - dict, - ], -) -def test_update_topic_rest(request_type): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"topic": {"name": "projects/sample1/topics/sample2"}} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.Topic( - name="name_value", - kms_key_name="kms_key_name_value", - satisfies_pzs=True, - state=pubsub.Topic.State.ACTIVE, - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.Topic.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.update_topic(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pubsub.Topic) - assert response.name == "name_value" - assert response.kms_key_name == "kms_key_name_value" - assert response.satisfies_pzs is True - assert response.state == pubsub.Topic.State.ACTIVE - - def test_update_topic_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -5459,81 +4832,6 @@ def test_update_topic_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_update_topic_rest_interceptors(null_interceptor): - transport = transports.PublisherRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), - ) - client = PublisherClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.PublisherRestInterceptor, "post_update_topic" - ) as post, mock.patch.object( - transports.PublisherRestInterceptor, "pre_update_topic" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.UpdateTopicRequest.pb(pubsub.UpdateTopicRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.Topic.to_json(pubsub.Topic()) - - request = pubsub.UpdateTopicRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.Topic() - - client.update_topic( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_update_topic_rest_bad_request( - transport: str = "rest", request_type=pubsub.UpdateTopicRequest -): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"topic": {"name": "projects/sample1/topics/sample2"}} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.update_topic(request) - - def test_update_topic_rest_flattened(): client = PublisherClient( credentials=ga_credentials.AnonymousCredentials(), @@ -5591,52 +4889,6 @@ def test_update_topic_rest_flattened_error(transport: str = "rest"): ) -def test_update_topic_rest_error(): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.PublishRequest, - dict, - ], -) -def test_publish_rest(request_type): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"topic": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.PublishResponse( - message_ids=["message_ids_value"], - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.PublishResponse.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.publish(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pubsub.PublishResponse) - assert response.message_ids == ["message_ids_value"] - - def test_publish_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -5763,83 +5015,6 @@ def test_publish_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_publish_rest_interceptors(null_interceptor): - transport = transports.PublisherRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), - ) - client = PublisherClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.PublisherRestInterceptor, "post_publish" - ) as post, mock.patch.object( - transports.PublisherRestInterceptor, "pre_publish" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.PublishRequest.pb(pubsub.PublishRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.PublishResponse.to_json( - pubsub.PublishResponse() - ) - - request = pubsub.PublishRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.PublishResponse() - - client.publish( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_publish_rest_bad_request( - transport: str = "rest", request_type=pubsub.PublishRequest -): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"topic": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.publish(request) - - def test_publish_rest_flattened(): client = PublisherClient( credentials=ga_credentials.AnonymousCredentials(), @@ -5898,70 +5073,18 @@ def test_publish_rest_flattened_error(transport: str = "rest"): ) -def test_publish_rest_error(): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) +def test_get_topic_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.GetTopicRequest, - dict, - ], -) -def test_get_topic_rest(request_type): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"topic": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.Topic( - name="name_value", - kms_key_name="kms_key_name_value", - satisfies_pzs=True, - state=pubsub.Topic.State.ACTIVE, - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.Topic.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.get_topic(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pubsub.Topic) - assert response.name == "name_value" - assert response.kms_key_name == "kms_key_name_value" - assert response.satisfies_pzs is True - assert response.state == pubsub.Topic.State.ACTIVE - - -def test_get_topic_rest_use_cached_wrapped_rpc(): - # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, - # instead of constructing them on each call - with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # Should wrap all calls on client creation - assert wrapper_fn.call_count > 0 - wrapper_fn.reset_mock() + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() # Ensure method has been cached assert client._transport.get_topic in client._transport._wrapped_methods @@ -6067,81 +5190,6 @@ def test_get_topic_rest_unset_required_fields(): assert set(unset_fields) == (set(()) & set(("topic",))) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_get_topic_rest_interceptors(null_interceptor): - transport = transports.PublisherRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), - ) - client = PublisherClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.PublisherRestInterceptor, "post_get_topic" - ) as post, mock.patch.object( - transports.PublisherRestInterceptor, "pre_get_topic" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.GetTopicRequest.pb(pubsub.GetTopicRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.Topic.to_json(pubsub.Topic()) - - request = pubsub.GetTopicRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.Topic() - - client.get_topic( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_get_topic_rest_bad_request( - transport: str = "rest", request_type=pubsub.GetTopicRequest -): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"topic": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.get_topic(request) - - def test_get_topic_rest_flattened(): client = PublisherClient( credentials=ga_credentials.AnonymousCredentials(), @@ -6197,52 +5245,6 @@ def test_get_topic_rest_flattened_error(transport: str = "rest"): ) -def test_get_topic_rest_error(): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.ListTopicsRequest, - dict, - ], -) -def test_list_topics_rest(request_type): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"project": "projects/sample1"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.ListTopicsResponse( - next_page_token="next_page_token_value", - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.ListTopicsResponse.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.list_topics(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pagers.ListTopicsPager) - assert response.next_page_token == "next_page_token_value" - - def test_list_topics_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -6375,83 +5377,6 @@ def test_list_topics_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_list_topics_rest_interceptors(null_interceptor): - transport = transports.PublisherRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), - ) - client = PublisherClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.PublisherRestInterceptor, "post_list_topics" - ) as post, mock.patch.object( - transports.PublisherRestInterceptor, "pre_list_topics" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.ListTopicsRequest.pb(pubsub.ListTopicsRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.ListTopicsResponse.to_json( - pubsub.ListTopicsResponse() - ) - - request = pubsub.ListTopicsRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.ListTopicsResponse() - - client.list_topics( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_list_topics_rest_bad_request( - transport: str = "rest", request_type=pubsub.ListTopicsRequest -): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"project": "projects/sample1"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.list_topics(request) - - def test_list_topics_rest_flattened(): client = PublisherClient( credentials=ga_credentials.AnonymousCredentials(), @@ -6568,56 +5493,14 @@ def test_list_topics_rest_pager(transport: str = "rest"): assert page_.raw_page.next_page_token == token -@pytest.mark.parametrize( - "request_type", - [ - pubsub.ListTopicSubscriptionsRequest, - dict, - ], -) -def test_list_topic_subscriptions_rest(request_type): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"topic": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.ListTopicSubscriptionsResponse( - subscriptions=["subscriptions_value"], - next_page_token="next_page_token_value", - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.ListTopicSubscriptionsResponse.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.list_topic_subscriptions(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pagers.ListTopicSubscriptionsPager) - assert response.subscriptions == ["subscriptions_value"] - assert response.next_page_token == "next_page_token_value" - - -def test_list_topic_subscriptions_rest_use_cached_wrapped_rpc(): - # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, - # instead of constructing them on each call - with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) +def test_list_topic_subscriptions_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) # Should wrap all calls on client creation assert wrapper_fn.call_count > 0 @@ -6749,85 +5632,6 @@ def test_list_topic_subscriptions_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_list_topic_subscriptions_rest_interceptors(null_interceptor): - transport = transports.PublisherRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), - ) - client = PublisherClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.PublisherRestInterceptor, "post_list_topic_subscriptions" - ) as post, mock.patch.object( - transports.PublisherRestInterceptor, "pre_list_topic_subscriptions" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.ListTopicSubscriptionsRequest.pb( - pubsub.ListTopicSubscriptionsRequest() - ) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.ListTopicSubscriptionsResponse.to_json( - pubsub.ListTopicSubscriptionsResponse() - ) - - request = pubsub.ListTopicSubscriptionsRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.ListTopicSubscriptionsResponse() - - client.list_topic_subscriptions( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_list_topic_subscriptions_rest_bad_request( - transport: str = "rest", request_type=pubsub.ListTopicSubscriptionsRequest -): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"topic": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.list_topic_subscriptions(request) - - def test_list_topic_subscriptions_rest_flattened(): client = PublisherClient( credentials=ga_credentials.AnonymousCredentials(), @@ -6947,48 +5751,6 @@ def test_list_topic_subscriptions_rest_pager(transport: str = "rest"): assert page_.raw_page.next_page_token == token -@pytest.mark.parametrize( - "request_type", - [ - pubsub.ListTopicSnapshotsRequest, - dict, - ], -) -def test_list_topic_snapshots_rest(request_type): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"topic": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.ListTopicSnapshotsResponse( - snapshots=["snapshots_value"], - next_page_token="next_page_token_value", - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.ListTopicSnapshotsResponse.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.list_topic_snapshots(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pagers.ListTopicSnapshotsPager) - assert response.snapshots == ["snapshots_value"] - assert response.next_page_token == "next_page_token_value" - - def test_list_topic_snapshots_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -7127,85 +5889,6 @@ def test_list_topic_snapshots_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_list_topic_snapshots_rest_interceptors(null_interceptor): - transport = transports.PublisherRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), - ) - client = PublisherClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.PublisherRestInterceptor, "post_list_topic_snapshots" - ) as post, mock.patch.object( - transports.PublisherRestInterceptor, "pre_list_topic_snapshots" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.ListTopicSnapshotsRequest.pb( - pubsub.ListTopicSnapshotsRequest() - ) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.ListTopicSnapshotsResponse.to_json( - pubsub.ListTopicSnapshotsResponse() - ) - - request = pubsub.ListTopicSnapshotsRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.ListTopicSnapshotsResponse() - - client.list_topic_snapshots( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_list_topic_snapshots_rest_bad_request( - transport: str = "rest", request_type=pubsub.ListTopicSnapshotsRequest -): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"topic": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.list_topic_snapshots(request) - - def test_list_topic_snapshots_rest_flattened(): client = PublisherClient( credentials=ga_credentials.AnonymousCredentials(), @@ -7323,53 +6006,18 @@ def test_list_topic_snapshots_rest_pager(transport: str = "rest"): assert page_.raw_page.next_page_token == token -@pytest.mark.parametrize( - "request_type", - [ - pubsub.DeleteTopicRequest, - dict, - ], -) -def test_delete_topic_rest(request_type): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) +def test_delete_topic_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) - # send a request that will satisfy transcoding - request_init = {"topic": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = None - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = "" - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.delete_topic(request) - - # Establish that the response is the type that we expect. - assert response is None - - -def test_delete_topic_rest_use_cached_wrapped_rpc(): - # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, - # instead of constructing them on each call - with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # Should wrap all calls on client creation - assert wrapper_fn.call_count > 0 - wrapper_fn.reset_mock() + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() # Ensure method has been cached assert client._transport.delete_topic in client._transport._wrapped_methods @@ -7472,75 +6120,6 @@ def test_delete_topic_rest_unset_required_fields(): assert set(unset_fields) == (set(()) & set(("topic",))) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_delete_topic_rest_interceptors(null_interceptor): - transport = transports.PublisherRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), - ) - client = PublisherClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.PublisherRestInterceptor, "pre_delete_topic" - ) as pre: - pre.assert_not_called() - pb_message = pubsub.DeleteTopicRequest.pb(pubsub.DeleteTopicRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - - request = pubsub.DeleteTopicRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - - client.delete_topic( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - - -def test_delete_topic_rest_bad_request( - transport: str = "rest", request_type=pubsub.DeleteTopicRequest -): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"topic": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.delete_topic(request) - - def test_delete_topic_rest_flattened(): client = PublisherClient( credentials=ga_credentials.AnonymousCredentials(), @@ -7594,49 +6173,6 @@ def test_delete_topic_rest_flattened_error(transport: str = "rest"): ) -def test_delete_topic_rest_error(): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.DetachSubscriptionRequest, - dict, - ], -) -def test_detach_subscription_rest(request_type): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.DetachSubscriptionResponse() - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.DetachSubscriptionResponse.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.detach_subscription(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pubsub.DetachSubscriptionResponse) - - def test_detach_subscription_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -7760,91 +6296,6 @@ def test_detach_subscription_rest_unset_required_fields(): assert set(unset_fields) == (set(()) & set(("subscription",))) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_detach_subscription_rest_interceptors(null_interceptor): - transport = transports.PublisherRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), - ) - client = PublisherClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.PublisherRestInterceptor, "post_detach_subscription" - ) as post, mock.patch.object( - transports.PublisherRestInterceptor, "pre_detach_subscription" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.DetachSubscriptionRequest.pb( - pubsub.DetachSubscriptionRequest() - ) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.DetachSubscriptionResponse.to_json( - pubsub.DetachSubscriptionResponse() - ) - - request = pubsub.DetachSubscriptionRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.DetachSubscriptionResponse() - - client.detach_subscription( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_detach_subscription_rest_bad_request( - transport: str = "rest", request_type=pubsub.DetachSubscriptionRequest -): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.detach_subscription(request) - - -def test_detach_subscription_rest_error(): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - def test_credentials_transport_error(): # It is an error to provide credentials and a transport instance. transport = transports.PublisherGrpcTransport( @@ -7921,34 +6372,1918 @@ def test_transport_get_channel(): assert channel -@pytest.mark.parametrize( - "transport_class", - [ - transports.PublisherGrpcTransport, - transports.PublisherGrpcAsyncIOTransport, - transports.PublisherRestTransport, - ], -) -def test_transport_adc(transport_class): - # Test default credentials are used if not provided. - with mock.patch.object(google.auth, "default") as adc: - adc.return_value = (ga_credentials.AnonymousCredentials(), None) - transport_class() - adc.assert_called_once() +@pytest.mark.parametrize( + "transport_class", + [ + transports.PublisherGrpcTransport, + transports.PublisherGrpcAsyncIOTransport, + transports.PublisherRestTransport, + ], +) +def test_transport_adc(transport_class): + # Test default credentials are used if not provided. + with mock.patch.object(google.auth, "default") as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class() + adc.assert_called_once() + + +def test_transport_kind_grpc(): + transport = PublisherClient.get_transport_class("grpc")( + credentials=ga_credentials.AnonymousCredentials() + ) + assert transport.kind == "grpc" + + +def test_initialize_client_w_grpc(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc" + ) + assert client is not None + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_create_topic_empty_call_grpc(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.create_topic), "__call__") as call: + call.return_value = pubsub.Topic() + client.create_topic(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.Topic() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_update_topic_empty_call_grpc(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.update_topic), "__call__") as call: + call.return_value = pubsub.Topic() + client.update_topic(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.UpdateTopicRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_publish_empty_call_grpc(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.publish), "__call__") as call: + call.return_value = pubsub.PublishResponse() + client.publish(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.PublishRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_get_topic_empty_call_grpc(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.get_topic), "__call__") as call: + call.return_value = pubsub.Topic() + client.get_topic(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.GetTopicRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_list_topics_empty_call_grpc(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.list_topics), "__call__") as call: + call.return_value = pubsub.ListTopicsResponse() + client.list_topics(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ListTopicsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_list_topic_subscriptions_empty_call_grpc(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.list_topic_subscriptions), "__call__" + ) as call: + call.return_value = pubsub.ListTopicSubscriptionsResponse() + client.list_topic_subscriptions(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ListTopicSubscriptionsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_list_topic_snapshots_empty_call_grpc(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.list_topic_snapshots), "__call__" + ) as call: + call.return_value = pubsub.ListTopicSnapshotsResponse() + client.list_topic_snapshots(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ListTopicSnapshotsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_delete_topic_empty_call_grpc(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.delete_topic), "__call__") as call: + call.return_value = None + client.delete_topic(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.DeleteTopicRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_detach_subscription_empty_call_grpc(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.detach_subscription), "__call__" + ) as call: + call.return_value = pubsub.DetachSubscriptionResponse() + client.detach_subscription(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.DetachSubscriptionRequest() + + assert args[0] == request_msg + + +def test_transport_kind_grpc_asyncio(): + transport = PublisherAsyncClient.get_transport_class("grpc_asyncio")( + credentials=async_anonymous_credentials() + ) + assert transport.kind == "grpc_asyncio" + + +def test_initialize_client_w_grpc_asyncio(): + client = PublisherAsyncClient( + credentials=async_anonymous_credentials(), transport="grpc_asyncio" + ) + assert client is not None + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_create_topic_empty_call_grpc_asyncio(): + client = PublisherAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.create_topic), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Topic( + name="name_value", + kms_key_name="kms_key_name_value", + satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, + ) + ) + await client.create_topic(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.Topic() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_update_topic_empty_call_grpc_asyncio(): + client = PublisherAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.update_topic), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Topic( + name="name_value", + kms_key_name="kms_key_name_value", + satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, + ) + ) + await client.update_topic(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.UpdateTopicRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_publish_empty_call_grpc_asyncio(): + client = PublisherAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.publish), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.PublishResponse( + message_ids=["message_ids_value"], + ) + ) + await client.publish(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.PublishRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_get_topic_empty_call_grpc_asyncio(): + client = PublisherAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.get_topic), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Topic( + name="name_value", + kms_key_name="kms_key_name_value", + satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, + ) + ) + await client.get_topic(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.GetTopicRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_list_topics_empty_call_grpc_asyncio(): + client = PublisherAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.list_topics), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.ListTopicsResponse( + next_page_token="next_page_token_value", + ) + ) + await client.list_topics(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ListTopicsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_list_topic_subscriptions_empty_call_grpc_asyncio(): + client = PublisherAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.list_topic_subscriptions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.ListTopicSubscriptionsResponse( + subscriptions=["subscriptions_value"], + next_page_token="next_page_token_value", + ) + ) + await client.list_topic_subscriptions(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ListTopicSubscriptionsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_list_topic_snapshots_empty_call_grpc_asyncio(): + client = PublisherAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.list_topic_snapshots), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.ListTopicSnapshotsResponse( + snapshots=["snapshots_value"], + next_page_token="next_page_token_value", + ) + ) + await client.list_topic_snapshots(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ListTopicSnapshotsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_delete_topic_empty_call_grpc_asyncio(): + client = PublisherAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.delete_topic), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + await client.delete_topic(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.DeleteTopicRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_detach_subscription_empty_call_grpc_asyncio(): + client = PublisherAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.detach_subscription), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.DetachSubscriptionResponse() + ) + await client.detach_subscription(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.DetachSubscriptionRequest() + + assert args[0] == request_msg + + +def test_transport_kind_rest(): + transport = PublisherClient.get_transport_class("rest")( + credentials=ga_credentials.AnonymousCredentials() + ) + assert transport.kind == "rest" + + +def test_create_topic_rest_bad_request(request_type=pubsub.Topic): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.create_topic(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.Topic, + dict, + ], +) +def test_create_topic_rest_call_success(request_type): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.Topic( + name="name_value", + kms_key_name="kms_key_name_value", + satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.Topic.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.create_topic(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pubsub.Topic) + assert response.name == "name_value" + assert response.kms_key_name == "kms_key_name_value" + assert response.satisfies_pzs is True + assert response.state == pubsub.Topic.State.ACTIVE + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_create_topic_rest_interceptors(null_interceptor): + transport = transports.PublisherRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), + ) + client = PublisherClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.PublisherRestInterceptor, "post_create_topic" + ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "pre_create_topic" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.Topic.pb(pubsub.Topic()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.Topic.to_json(pubsub.Topic()) + req.return_value.content = return_value + + request = pubsub.Topic() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.Topic() + + client.create_topic( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_update_topic_rest_bad_request(request_type=pubsub.UpdateTopicRequest): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"topic": {"name": "projects/sample1/topics/sample2"}} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.update_topic(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.UpdateTopicRequest, + dict, + ], +) +def test_update_topic_rest_call_success(request_type): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"topic": {"name": "projects/sample1/topics/sample2"}} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.Topic( + name="name_value", + kms_key_name="kms_key_name_value", + satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.Topic.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.update_topic(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pubsub.Topic) + assert response.name == "name_value" + assert response.kms_key_name == "kms_key_name_value" + assert response.satisfies_pzs is True + assert response.state == pubsub.Topic.State.ACTIVE + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_update_topic_rest_interceptors(null_interceptor): + transport = transports.PublisherRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), + ) + client = PublisherClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.PublisherRestInterceptor, "post_update_topic" + ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "pre_update_topic" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.UpdateTopicRequest.pb(pubsub.UpdateTopicRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.Topic.to_json(pubsub.Topic()) + req.return_value.content = return_value + + request = pubsub.UpdateTopicRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.Topic() + + client.update_topic( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_publish_rest_bad_request(request_type=pubsub.PublishRequest): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"topic": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.publish(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.PublishRequest, + dict, + ], +) +def test_publish_rest_call_success(request_type): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"topic": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.PublishResponse( + message_ids=["message_ids_value"], + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.PublishResponse.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.publish(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pubsub.PublishResponse) + assert response.message_ids == ["message_ids_value"] + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_publish_rest_interceptors(null_interceptor): + transport = transports.PublisherRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), + ) + client = PublisherClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.PublisherRestInterceptor, "post_publish" + ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "pre_publish" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.PublishRequest.pb(pubsub.PublishRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.PublishResponse.to_json(pubsub.PublishResponse()) + req.return_value.content = return_value + + request = pubsub.PublishRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.PublishResponse() + + client.publish( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_get_topic_rest_bad_request(request_type=pubsub.GetTopicRequest): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"topic": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.get_topic(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.GetTopicRequest, + dict, + ], +) +def test_get_topic_rest_call_success(request_type): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"topic": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.Topic( + name="name_value", + kms_key_name="kms_key_name_value", + satisfies_pzs=True, + state=pubsub.Topic.State.ACTIVE, + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.Topic.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.get_topic(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pubsub.Topic) + assert response.name == "name_value" + assert response.kms_key_name == "kms_key_name_value" + assert response.satisfies_pzs is True + assert response.state == pubsub.Topic.State.ACTIVE + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_get_topic_rest_interceptors(null_interceptor): + transport = transports.PublisherRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), + ) + client = PublisherClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.PublisherRestInterceptor, "post_get_topic" + ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "pre_get_topic" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.GetTopicRequest.pb(pubsub.GetTopicRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.Topic.to_json(pubsub.Topic()) + req.return_value.content = return_value + + request = pubsub.GetTopicRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.Topic() + + client.get_topic( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_list_topics_rest_bad_request(request_type=pubsub.ListTopicsRequest): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"project": "projects/sample1"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.list_topics(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.ListTopicsRequest, + dict, + ], +) +def test_list_topics_rest_call_success(request_type): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"project": "projects/sample1"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.ListTopicsResponse( + next_page_token="next_page_token_value", + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.ListTopicsResponse.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.list_topics(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListTopicsPager) + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_list_topics_rest_interceptors(null_interceptor): + transport = transports.PublisherRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), + ) + client = PublisherClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.PublisherRestInterceptor, "post_list_topics" + ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "pre_list_topics" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.ListTopicsRequest.pb(pubsub.ListTopicsRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.ListTopicsResponse.to_json(pubsub.ListTopicsResponse()) + req.return_value.content = return_value + + request = pubsub.ListTopicsRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.ListTopicsResponse() + + client.list_topics( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_list_topic_subscriptions_rest_bad_request( + request_type=pubsub.ListTopicSubscriptionsRequest, +): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"topic": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.list_topic_subscriptions(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.ListTopicSubscriptionsRequest, + dict, + ], +) +def test_list_topic_subscriptions_rest_call_success(request_type): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"topic": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.ListTopicSubscriptionsResponse( + subscriptions=["subscriptions_value"], + next_page_token="next_page_token_value", + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.ListTopicSubscriptionsResponse.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.list_topic_subscriptions(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListTopicSubscriptionsPager) + assert response.subscriptions == ["subscriptions_value"] + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_list_topic_subscriptions_rest_interceptors(null_interceptor): + transport = transports.PublisherRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), + ) + client = PublisherClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.PublisherRestInterceptor, "post_list_topic_subscriptions" + ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "pre_list_topic_subscriptions" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.ListTopicSubscriptionsRequest.pb( + pubsub.ListTopicSubscriptionsRequest() + ) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.ListTopicSubscriptionsResponse.to_json( + pubsub.ListTopicSubscriptionsResponse() + ) + req.return_value.content = return_value + + request = pubsub.ListTopicSubscriptionsRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.ListTopicSubscriptionsResponse() + + client.list_topic_subscriptions( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_list_topic_snapshots_rest_bad_request( + request_type=pubsub.ListTopicSnapshotsRequest, +): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"topic": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.list_topic_snapshots(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.ListTopicSnapshotsRequest, + dict, + ], +) +def test_list_topic_snapshots_rest_call_success(request_type): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"topic": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.ListTopicSnapshotsResponse( + snapshots=["snapshots_value"], + next_page_token="next_page_token_value", + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.ListTopicSnapshotsResponse.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.list_topic_snapshots(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListTopicSnapshotsPager) + assert response.snapshots == ["snapshots_value"] + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_list_topic_snapshots_rest_interceptors(null_interceptor): + transport = transports.PublisherRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), + ) + client = PublisherClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.PublisherRestInterceptor, "post_list_topic_snapshots" + ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "pre_list_topic_snapshots" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.ListTopicSnapshotsRequest.pb( + pubsub.ListTopicSnapshotsRequest() + ) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.ListTopicSnapshotsResponse.to_json( + pubsub.ListTopicSnapshotsResponse() + ) + req.return_value.content = return_value + + request = pubsub.ListTopicSnapshotsRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.ListTopicSnapshotsResponse() + + client.list_topic_snapshots( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_delete_topic_rest_bad_request(request_type=pubsub.DeleteTopicRequest): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"topic": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.delete_topic(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.DeleteTopicRequest, + dict, + ], +) +def test_delete_topic_rest_call_success(request_type): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"topic": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = None + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = "" + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.delete_topic(request) + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_delete_topic_rest_interceptors(null_interceptor): + transport = transports.PublisherRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), + ) + client = PublisherClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.PublisherRestInterceptor, "pre_delete_topic" + ) as pre: + pre.assert_not_called() + pb_message = pubsub.DeleteTopicRequest.pb(pubsub.DeleteTopicRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + + request = pubsub.DeleteTopicRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + + client.delete_topic( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + + +def test_detach_subscription_rest_bad_request( + request_type=pubsub.DetachSubscriptionRequest, +): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.detach_subscription(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.DetachSubscriptionRequest, + dict, + ], +) +def test_detach_subscription_rest_call_success(request_type): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.DetachSubscriptionResponse() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.DetachSubscriptionResponse.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.detach_subscription(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pubsub.DetachSubscriptionResponse) + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_detach_subscription_rest_interceptors(null_interceptor): + transport = transports.PublisherRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None if null_interceptor else transports.PublisherRestInterceptor(), + ) + client = PublisherClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.PublisherRestInterceptor, "post_detach_subscription" + ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "pre_detach_subscription" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.DetachSubscriptionRequest.pb( + pubsub.DetachSubscriptionRequest() + ) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.DetachSubscriptionResponse.to_json( + pubsub.DetachSubscriptionResponse() + ) + req.return_value.content = return_value + + request = pubsub.DetachSubscriptionRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.DetachSubscriptionResponse() + + client.detach_subscription( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_get_iam_policy_rest_bad_request( + request_type=iam_policy_pb2.GetIamPolicyRequest, +): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type() + request = json_format.ParseDict( + {"resource": "projects/sample1/topics/sample2"}, request + ) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = Response() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + client.get_iam_policy(request) + + +@pytest.mark.parametrize( + "request_type", + [ + iam_policy_pb2.GetIamPolicyRequest, + dict, + ], +) +def test_get_iam_policy_rest(request_type): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + request_init = {"resource": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "request") as req: + # Designate an appropriate value for the returned response. + return_value = policy_pb2.Policy() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + + req.return_value = response_value + + response = client.get_iam_policy(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + + +def test_set_iam_policy_rest_bad_request( + request_type=iam_policy_pb2.SetIamPolicyRequest, +): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type() + request = json_format.ParseDict( + {"resource": "projects/sample1/topics/sample2"}, request + ) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = Response() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + client.set_iam_policy(request) + + +@pytest.mark.parametrize( + "request_type", + [ + iam_policy_pb2.SetIamPolicyRequest, + dict, + ], +) +def test_set_iam_policy_rest(request_type): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + request_init = {"resource": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "request") as req: + # Designate an appropriate value for the returned response. + return_value = policy_pb2.Policy() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + + req.return_value = response_value + + response = client.set_iam_policy(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + + +def test_test_iam_permissions_rest_bad_request( + request_type=iam_policy_pb2.TestIamPermissionsRequest, +): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type() + request = json_format.ParseDict( + {"resource": "projects/sample1/subscriptions/sample2"}, request + ) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = Response() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + client.test_iam_permissions(request) + + +@pytest.mark.parametrize( + "request_type", + [ + iam_policy_pb2.TestIamPermissionsRequest, + dict, + ], +) +def test_test_iam_permissions_rest(request_type): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + request_init = {"resource": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "request") as req: + # Designate an appropriate value for the returned response. + return_value = iam_policy_pb2.TestIamPermissionsResponse() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + + req.return_value = response_value + + response = client.test_iam_permissions(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) + + +def test_initialize_client_w_rest(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + assert client is not None + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_create_topic_empty_call_rest(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.create_topic), "__call__") as call: + client.create_topic(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.Topic() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_update_topic_empty_call_rest(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.update_topic), "__call__") as call: + client.update_topic(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.UpdateTopicRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_publish_empty_call_rest(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.publish), "__call__") as call: + client.publish(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.PublishRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_get_topic_empty_call_rest(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.get_topic), "__call__") as call: + client.get_topic(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.GetTopicRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_list_topics_empty_call_rest(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.list_topics), "__call__") as call: + client.list_topics(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ListTopicsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_list_topic_subscriptions_empty_call_rest(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.list_topic_subscriptions), "__call__" + ) as call: + client.list_topic_subscriptions(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ListTopicSubscriptionsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_list_topic_snapshots_empty_call_rest(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.list_topic_snapshots), "__call__" + ) as call: + client.list_topic_snapshots(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ListTopicSnapshotsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_delete_topic_empty_call_rest(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.delete_topic), "__call__") as call: + client.delete_topic(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.DeleteTopicRequest() + + assert args[0] == request_msg -@pytest.mark.parametrize( - "transport_name", - [ - "grpc", - "rest", - ], -) -def test_transport_kind(transport_name): - transport = PublisherClient.get_transport_class(transport_name)( +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_detach_subscription_empty_call_rest(): + client = PublisherClient( credentials=ga_credentials.AnonymousCredentials(), + transport="rest", ) - assert transport.kind == transport_name + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.detach_subscription), "__call__" + ) as call: + client.detach_subscription(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.DetachSubscriptionRequest() + + assert args[0] == request_msg def test_transport_grpc_default(): @@ -8613,194 +8948,6 @@ def test_client_with_default_client_info(): prep.assert_called_once_with(client_info) -@pytest.mark.asyncio -async def test_transport_close_async(): - client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - with mock.patch.object( - type(getattr(client.transport, "grpc_channel")), "close" - ) as close: - async with client: - close.assert_not_called() - close.assert_called_once() - - -def test_get_iam_policy_rest_bad_request( - transport: str = "rest", request_type=iam_policy_pb2.GetIamPolicyRequest -): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - request = request_type() - request = json_format.ParseDict( - {"resource": "projects/sample1/topics/sample2"}, request - ) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.get_iam_policy(request) - - -@pytest.mark.parametrize( - "request_type", - [ - iam_policy_pb2.GetIamPolicyRequest, - dict, - ], -) -def test_get_iam_policy_rest(request_type): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - request_init = {"resource": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = policy_pb2.Policy() - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - - response = client.get_iam_policy(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, policy_pb2.Policy) - - -def test_set_iam_policy_rest_bad_request( - transport: str = "rest", request_type=iam_policy_pb2.SetIamPolicyRequest -): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - request = request_type() - request = json_format.ParseDict( - {"resource": "projects/sample1/topics/sample2"}, request - ) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.set_iam_policy(request) - - -@pytest.mark.parametrize( - "request_type", - [ - iam_policy_pb2.SetIamPolicyRequest, - dict, - ], -) -def test_set_iam_policy_rest(request_type): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - request_init = {"resource": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = policy_pb2.Policy() - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - - response = client.set_iam_policy(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, policy_pb2.Policy) - - -def test_test_iam_permissions_rest_bad_request( - transport: str = "rest", request_type=iam_policy_pb2.TestIamPermissionsRequest -): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - request = request_type() - request = json_format.ParseDict( - {"resource": "projects/sample1/subscriptions/sample2"}, request - ) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.test_iam_permissions(request) - - -@pytest.mark.parametrize( - "request_type", - [ - iam_policy_pb2.TestIamPermissionsRequest, - dict, - ], -) -def test_test_iam_permissions_rest(request_type): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - request_init = {"resource": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = iam_policy_pb2.TestIamPermissionsResponse() - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - - response = client.test_iam_permissions(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) - - def test_set_iam_policy(transport: str = "grpc"): client = PublisherClient( credentials=ga_credentials.AnonymousCredentials(), @@ -8836,7 +8983,7 @@ def test_set_iam_policy(transport: str = "grpc"): @pytest.mark.asyncio async def test_set_iam_policy_async(transport: str = "grpc_asyncio"): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -8901,7 +9048,7 @@ def test_set_iam_policy_field_headers(): @pytest.mark.asyncio async def test_set_iam_policy_field_headers_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -8949,7 +9096,7 @@ def test_set_iam_policy_from_dict(): @pytest.mark.asyncio async def test_set_iam_policy_from_dict_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: @@ -9002,7 +9149,7 @@ def test_get_iam_policy(transport: str = "grpc"): @pytest.mark.asyncio async def test_get_iam_policy_async(transport: str = "grpc_asyncio"): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -9068,7 +9215,7 @@ def test_get_iam_policy_field_headers(): @pytest.mark.asyncio async def test_get_iam_policy_field_headers_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -9116,7 +9263,7 @@ def test_get_iam_policy_from_dict(): @pytest.mark.asyncio async def test_get_iam_policy_from_dict_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: @@ -9168,7 +9315,7 @@ def test_test_iam_permissions(transport: str = "grpc"): @pytest.mark.asyncio async def test_test_iam_permissions_async(transport: str = "grpc_asyncio"): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -9235,7 +9382,7 @@ def test_test_iam_permissions_field_headers(): @pytest.mark.asyncio async def test_test_iam_permissions_field_headers_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -9289,7 +9436,7 @@ def test_test_iam_permissions_from_dict(): @pytest.mark.asyncio async def test_test_iam_permissions_from_dict_async(): client = PublisherAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( @@ -9309,22 +9456,41 @@ async def test_test_iam_permissions_from_dict_async(): call.assert_called() -def test_transport_close(): - transports = { - "rest": "_session", - "grpc": "_grpc_channel", - } +def test_transport_close_grpc(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc" + ) + with mock.patch.object( + type(getattr(client.transport, "_grpc_channel")), "close" + ) as close: + with client: + close.assert_not_called() + close.assert_called_once() - for transport, close_name in transports.items(): - client = PublisherClient( - credentials=ga_credentials.AnonymousCredentials(), transport=transport - ) - with mock.patch.object( - type(getattr(client.transport, close_name)), "close" - ) as close: - with client: - close.assert_not_called() - close.assert_called_once() + +@pytest.mark.asyncio +async def test_transport_close_grpc_asyncio(): + client = PublisherAsyncClient( + credentials=async_anonymous_credentials(), transport="grpc_asyncio" + ) + with mock.patch.object( + type(getattr(client.transport, "_grpc_channel")), "close" + ) as close: + async with client: + close.assert_not_called() + close.assert_called_once() + + +def test_transport_close_rest(): + client = PublisherClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + with mock.patch.object( + type(getattr(client.transport, "_session")), "close" + ) as close: + with client: + close.assert_not_called() + close.assert_called_once() def test_client_ctx(): diff --git a/tests/unit/gapic/pubsub_v1/test_schema_service.py b/tests/unit/gapic/pubsub_v1/test_schema_service.py index 7be518378..3dbefc470 100644 --- a/tests/unit/gapic/pubsub_v1/test_schema_service.py +++ b/tests/unit/gapic/pubsub_v1/test_schema_service.py @@ -24,7 +24,7 @@ import grpc from grpc.experimental import aio -from collections.abc import Iterable +from collections.abc import Iterable, AsyncIterable from google.protobuf import json_format import json import math @@ -37,6 +37,13 @@ from requests.sessions import Session from google.protobuf import json_format +try: + from google.auth.aio import credentials as ga_credentials_async + + HAS_GOOGLE_AUTH_AIO = True +except ImportError: # pragma: NO COVER + HAS_GOOGLE_AUTH_AIO = False + from google.api_core import client_options from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 @@ -60,10 +67,24 @@ import google.auth +async def mock_async_gen(data, chunk_size=1): + for i in range(0, len(data)): # pragma: NO COVER + chunk = data[i : i + chunk_size] + yield chunk.encode("utf-8") + + def client_cert_source_callback(): return b"cert bytes", b"key bytes" +# TODO: use async auth anon credentials by default once the minimum version of google-auth is upgraded. +# See related issue: https://github.com/googleapis/gapic-generator-python/issues/2107. +def async_anonymous_credentials(): + if HAS_GOOGLE_AUTH_AIO: + return ga_credentials_async.AnonymousCredentials() + return ga_credentials.AnonymousCredentials() + + # If default endpoint is localhost, then default mtls endpoint will be the same. # This method modifies the default endpoint so the client can produce a different # mtls endpoint for endpoint testing purposes. @@ -299,86 +320,6 @@ def test__get_universe_domain(): assert str(excinfo.value) == "Universe Domain cannot be an empty string." -@pytest.mark.parametrize( - "client_class,transport_class,transport_name", - [ - (SchemaServiceClient, transports.SchemaServiceGrpcTransport, "grpc"), - (SchemaServiceClient, transports.SchemaServiceRestTransport, "rest"), - ], -) -def test__validate_universe_domain(client_class, transport_class, transport_name): - client = client_class( - transport=transport_class(credentials=ga_credentials.AnonymousCredentials()) - ) - assert client._validate_universe_domain() == True - - # Test the case when universe is already validated. - assert client._validate_universe_domain() == True - - if transport_name == "grpc": - # Test the case where credentials are provided by the - # `local_channel_credentials`. The default universes in both match. - channel = grpc.secure_channel( - "http://localhost/", grpc.local_channel_credentials() - ) - client = client_class(transport=transport_class(channel=channel)) - assert client._validate_universe_domain() == True - - # Test the case where credentials do not exist: e.g. a transport is provided - # with no credentials. Validation should still succeed because there is no - # mismatch with non-existent credentials. - channel = grpc.secure_channel( - "http://localhost/", grpc.local_channel_credentials() - ) - transport = transport_class(channel=channel) - transport._credentials = None - client = client_class(transport=transport) - assert client._validate_universe_domain() == True - - # TODO: This is needed to cater for older versions of google-auth - # Make this test unconditional once the minimum supported version of - # google-auth becomes 2.23.0 or higher. - google_auth_major, google_auth_minor = [ - int(part) for part in google.auth.__version__.split(".")[0:2] - ] - if google_auth_major > 2 or (google_auth_major == 2 and google_auth_minor >= 23): - credentials = ga_credentials.AnonymousCredentials() - credentials._universe_domain = "foo.com" - # Test the case when there is a universe mismatch from the credentials. - client = client_class(transport=transport_class(credentials=credentials)) - with pytest.raises(ValueError) as excinfo: - client._validate_universe_domain() - assert ( - str(excinfo.value) - == "The configured universe domain (googleapis.com) does not match the universe domain found in the credentials (foo.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default." - ) - - # Test the case when there is a universe mismatch from the client. - # - # TODO: Make this test unconditional once the minimum supported version of - # google-api-core becomes 2.15.0 or higher. - api_core_major, api_core_minor = [ - int(part) for part in api_core_version.__version__.split(".")[0:2] - ] - if api_core_major > 2 or (api_core_major == 2 and api_core_minor >= 15): - client = client_class( - client_options={"universe_domain": "bar.com"}, - transport=transport_class( - credentials=ga_credentials.AnonymousCredentials(), - ), - ) - with pytest.raises(ValueError) as excinfo: - client._validate_universe_domain() - assert ( - str(excinfo.value) - == "The configured universe domain (bar.com) does not match the universe domain found in the credentials (googleapis.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default." - ) - - # Test that ValueError is raised if universe_domain is provided via client options and credentials is None - with pytest.raises(ValueError): - client._compare_universes("foo.bar", None) - - @pytest.mark.parametrize( "client_class,transport_name", [ @@ -1168,25 +1109,6 @@ def test_create_schema(request_type, transport: str = "grpc"): assert response.revision_id == "revision_id_value" -def test_create_schema_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.create_schema), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.create_schema() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == gp_schema.CreateSchemaRequest() - - def test_create_schema_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -1252,32 +1174,6 @@ def test_create_schema_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_create_schema_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.create_schema), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - gp_schema.Schema( - name="name_value", - type_=gp_schema.Schema.Type.PROTOCOL_BUFFER, - definition="definition_value", - revision_id="revision_id_value", - ) - ) - response = await client.create_schema() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == gp_schema.CreateSchemaRequest() - - @pytest.mark.asyncio async def test_create_schema_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -1286,7 +1182,7 @@ async def test_create_schema_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -1325,7 +1221,7 @@ async def test_create_schema_async( transport: str = "grpc_asyncio", request_type=gp_schema.CreateSchemaRequest ): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -1397,7 +1293,7 @@ def test_create_schema_field_headers(): @pytest.mark.asyncio async def test_create_schema_field_headers_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -1475,7 +1371,7 @@ def test_create_schema_flattened_error(): @pytest.mark.asyncio async def test_create_schema_flattened_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -1510,7 +1406,7 @@ async def test_create_schema_flattened_async(): @pytest.mark.asyncio async def test_create_schema_flattened_error_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -1566,25 +1462,6 @@ def test_get_schema(request_type, transport: str = "grpc"): assert response.revision_id == "revision_id_value" -def test_get_schema_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.get_schema), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.get_schema() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == schema.GetSchemaRequest() - - def test_get_schema_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -1648,39 +1525,13 @@ def test_get_schema_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_get_schema_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.get_schema), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - schema.Schema( - name="name_value", - type_=schema.Schema.Type.PROTOCOL_BUFFER, - definition="definition_value", - revision_id="revision_id_value", - ) - ) - response = await client.get_schema() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == schema.GetSchemaRequest() - - @pytest.mark.asyncio async def test_get_schema_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio"): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -1719,7 +1570,7 @@ async def test_get_schema_async( transport: str = "grpc_asyncio", request_type=schema.GetSchemaRequest ): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -1791,7 +1642,7 @@ def test_get_schema_field_headers(): @pytest.mark.asyncio async def test_get_schema_field_headers_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -1859,7 +1710,7 @@ def test_get_schema_flattened_error(): @pytest.mark.asyncio async def test_get_schema_flattened_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -1886,7 +1737,7 @@ async def test_get_schema_flattened_async(): @pytest.mark.asyncio async def test_get_schema_flattened_error_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -1934,25 +1785,6 @@ def test_list_schemas(request_type, transport: str = "grpc"): assert response.next_page_token == "next_page_token_value" -def test_list_schemas_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.list_schemas), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.list_schemas() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == schema.ListSchemasRequest() - - def test_list_schemas_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -2018,29 +1850,6 @@ def test_list_schemas_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_list_schemas_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.list_schemas), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - schema.ListSchemasResponse( - next_page_token="next_page_token_value", - ) - ) - response = await client.list_schemas() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == schema.ListSchemasRequest() - - @pytest.mark.asyncio async def test_list_schemas_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -2049,7 +1858,7 @@ async def test_list_schemas_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -2088,7 +1897,7 @@ async def test_list_schemas_async( transport: str = "grpc_asyncio", request_type=schema.ListSchemasRequest ): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -2154,7 +1963,7 @@ def test_list_schemas_field_headers(): @pytest.mark.asyncio async def test_list_schemas_field_headers_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -2224,7 +2033,7 @@ def test_list_schemas_flattened_error(): @pytest.mark.asyncio async def test_list_schemas_flattened_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2253,7 +2062,7 @@ async def test_list_schemas_flattened_async(): @pytest.mark.asyncio async def test_list_schemas_flattened_error_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -2363,7 +2172,7 @@ def test_list_schemas_pages(transport_name: str = "grpc"): @pytest.mark.asyncio async def test_list_schemas_async_pager(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2413,7 +2222,7 @@ async def test_list_schemas_async_pager(): @pytest.mark.asyncio async def test_list_schemas_async_pages(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2497,27 +2306,6 @@ def test_list_schema_revisions(request_type, transport: str = "grpc"): assert response.next_page_token == "next_page_token_value" -def test_list_schema_revisions_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.list_schema_revisions), "__call__" - ) as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.list_schema_revisions() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == schema.ListSchemaRevisionsRequest() - - def test_list_schema_revisions_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -2590,31 +2378,6 @@ def test_list_schema_revisions_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_list_schema_revisions_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.list_schema_revisions), "__call__" - ) as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - schema.ListSchemaRevisionsResponse( - next_page_token="next_page_token_value", - ) - ) - response = await client.list_schema_revisions() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == schema.ListSchemaRevisionsRequest() - - @pytest.mark.asyncio async def test_list_schema_revisions_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -2623,7 +2386,7 @@ async def test_list_schema_revisions_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -2662,7 +2425,7 @@ async def test_list_schema_revisions_async( transport: str = "grpc_asyncio", request_type=schema.ListSchemaRevisionsRequest ): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -2732,7 +2495,7 @@ def test_list_schema_revisions_field_headers(): @pytest.mark.asyncio async def test_list_schema_revisions_field_headers_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -2806,7 +2569,7 @@ def test_list_schema_revisions_flattened_error(): @pytest.mark.asyncio async def test_list_schema_revisions_flattened_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2837,7 +2600,7 @@ async def test_list_schema_revisions_flattened_async(): @pytest.mark.asyncio async def test_list_schema_revisions_flattened_error_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -2951,7 +2714,7 @@ def test_list_schema_revisions_pages(transport_name: str = "grpc"): @pytest.mark.asyncio async def test_list_schema_revisions_async_pager(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -3003,7 +2766,7 @@ async def test_list_schema_revisions_async_pager(): @pytest.mark.asyncio async def test_list_schema_revisions_async_pages(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -3093,25 +2856,6 @@ def test_commit_schema(request_type, transport: str = "grpc"): assert response.revision_id == "revision_id_value" -def test_commit_schema_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.commit_schema), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.commit_schema() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == gp_schema.CommitSchemaRequest() - - def test_commit_schema_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -3175,32 +2919,6 @@ def test_commit_schema_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_commit_schema_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.commit_schema), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - gp_schema.Schema( - name="name_value", - type_=gp_schema.Schema.Type.PROTOCOL_BUFFER, - definition="definition_value", - revision_id="revision_id_value", - ) - ) - response = await client.commit_schema() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == gp_schema.CommitSchemaRequest() - - @pytest.mark.asyncio async def test_commit_schema_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -3209,7 +2927,7 @@ async def test_commit_schema_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -3248,7 +2966,7 @@ async def test_commit_schema_async( transport: str = "grpc_asyncio", request_type=gp_schema.CommitSchemaRequest ): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -3320,7 +3038,7 @@ def test_commit_schema_field_headers(): @pytest.mark.asyncio async def test_commit_schema_field_headers_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -3393,7 +3111,7 @@ def test_commit_schema_flattened_error(): @pytest.mark.asyncio async def test_commit_schema_flattened_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -3424,7 +3142,7 @@ async def test_commit_schema_flattened_async(): @pytest.mark.asyncio async def test_commit_schema_flattened_error_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -3479,25 +3197,6 @@ def test_rollback_schema(request_type, transport: str = "grpc"): assert response.revision_id == "revision_id_value" -def test_rollback_schema_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.rollback_schema), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.rollback_schema() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == schema.RollbackSchemaRequest() - - def test_rollback_schema_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -3563,32 +3262,6 @@ def test_rollback_schema_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_rollback_schema_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.rollback_schema), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - schema.Schema( - name="name_value", - type_=schema.Schema.Type.PROTOCOL_BUFFER, - definition="definition_value", - revision_id="revision_id_value", - ) - ) - response = await client.rollback_schema() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == schema.RollbackSchemaRequest() - - @pytest.mark.asyncio async def test_rollback_schema_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -3597,7 +3270,7 @@ async def test_rollback_schema_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -3636,7 +3309,7 @@ async def test_rollback_schema_async( transport: str = "grpc_asyncio", request_type=schema.RollbackSchemaRequest ): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -3708,7 +3381,7 @@ def test_rollback_schema_field_headers(): @pytest.mark.asyncio async def test_rollback_schema_field_headers_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -3781,7 +3454,7 @@ def test_rollback_schema_flattened_error(): @pytest.mark.asyncio async def test_rollback_schema_flattened_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -3812,7 +3485,7 @@ async def test_rollback_schema_flattened_async(): @pytest.mark.asyncio async def test_rollback_schema_flattened_error_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -3869,27 +3542,6 @@ def test_delete_schema_revision(request_type, transport: str = "grpc"): assert response.revision_id == "revision_id_value" -def test_delete_schema_revision_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.delete_schema_revision), "__call__" - ) as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.delete_schema_revision() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == schema.DeleteSchemaRevisionRequest() - - def test_delete_schema_revision_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -3962,34 +3614,6 @@ def test_delete_schema_revision_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_delete_schema_revision_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.delete_schema_revision), "__call__" - ) as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - schema.Schema( - name="name_value", - type_=schema.Schema.Type.PROTOCOL_BUFFER, - definition="definition_value", - revision_id="revision_id_value", - ) - ) - response = await client.delete_schema_revision() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == schema.DeleteSchemaRevisionRequest() - - @pytest.mark.asyncio async def test_delete_schema_revision_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -3998,7 +3622,7 @@ async def test_delete_schema_revision_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4037,7 +3661,7 @@ async def test_delete_schema_revision_async( transport: str = "grpc_asyncio", request_type=schema.DeleteSchemaRevisionRequest ): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4113,7 +3737,7 @@ def test_delete_schema_revision_field_headers(): @pytest.mark.asyncio async def test_delete_schema_revision_field_headers_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -4190,7 +3814,7 @@ def test_delete_schema_revision_flattened_error(): @pytest.mark.asyncio async def test_delete_schema_revision_flattened_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -4223,7 +3847,7 @@ async def test_delete_schema_revision_flattened_async(): @pytest.mark.asyncio async def test_delete_schema_revision_flattened_error_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -4269,25 +3893,6 @@ def test_delete_schema(request_type, transport: str = "grpc"): assert response is None -def test_delete_schema_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.delete_schema), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.delete_schema() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == schema.DeleteSchemaRequest() - - def test_delete_schema_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -4351,25 +3956,6 @@ def test_delete_schema_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_delete_schema_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.delete_schema), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) - response = await client.delete_schema() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == schema.DeleteSchemaRequest() - - @pytest.mark.asyncio async def test_delete_schema_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -4378,7 +3964,7 @@ async def test_delete_schema_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4417,7 +4003,7 @@ async def test_delete_schema_async( transport: str = "grpc_asyncio", request_type=schema.DeleteSchemaRequest ): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4478,7 +4064,7 @@ def test_delete_schema_field_headers(): @pytest.mark.asyncio async def test_delete_schema_field_headers_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -4546,7 +4132,7 @@ def test_delete_schema_flattened_error(): @pytest.mark.asyncio async def test_delete_schema_flattened_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -4573,7 +4159,7 @@ async def test_delete_schema_flattened_async(): @pytest.mark.asyncio async def test_delete_schema_flattened_error_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -4618,25 +4204,6 @@ def test_validate_schema(request_type, transport: str = "grpc"): assert isinstance(response, gp_schema.ValidateSchemaResponse) -def test_validate_schema_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.validate_schema), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.validate_schema() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == gp_schema.ValidateSchemaRequest() - - def test_validate_schema_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -4700,27 +4267,6 @@ def test_validate_schema_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_validate_schema_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.validate_schema), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - gp_schema.ValidateSchemaResponse() - ) - response = await client.validate_schema() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == gp_schema.ValidateSchemaRequest() - - @pytest.mark.asyncio async def test_validate_schema_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -4729,7 +4275,7 @@ async def test_validate_schema_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4768,7 +4314,7 @@ async def test_validate_schema_async( transport: str = "grpc_asyncio", request_type=gp_schema.ValidateSchemaRequest ): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4831,7 +4377,7 @@ def test_validate_schema_field_headers(): @pytest.mark.asyncio async def test_validate_schema_field_headers_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -4906,7 +4452,7 @@ def test_validate_schema_flattened_error(): @pytest.mark.asyncio async def test_validate_schema_flattened_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -4939,7 +4485,7 @@ async def test_validate_schema_flattened_async(): @pytest.mark.asyncio async def test_validate_schema_flattened_error_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -4985,25 +4531,6 @@ def test_validate_message(request_type, transport: str = "grpc"): assert isinstance(response, schema.ValidateMessageResponse) -def test_validate_message_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.validate_message), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.validate_message() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == schema.ValidateMessageRequest() - - def test_validate_message_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -5071,27 +4598,6 @@ def test_validate_message_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_validate_message_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.validate_message), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - schema.ValidateMessageResponse() - ) - response = await client.validate_message() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == schema.ValidateMessageRequest() - - @pytest.mark.asyncio async def test_validate_message_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -5100,7 +4606,7 @@ async def test_validate_message_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -5139,7 +4645,7 @@ async def test_validate_message_async( transport: str = "grpc_asyncio", request_type=schema.ValidateMessageRequest ): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -5202,7 +4708,7 @@ def test_validate_message_field_headers(): @pytest.mark.asyncio async def test_validate_message_field_headers_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -5231,160 +4737,40 @@ async def test_validate_message_field_headers_async(): ) in kw["metadata"] -@pytest.mark.parametrize( - "request_type", - [ - gp_schema.CreateSchemaRequest, - dict, - ], -) -def test_create_schema_rest(request_type): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) +def test_create_schema_rest_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) - # send a request that will satisfy transcoding - request_init = {"parent": "projects/sample1"} - request_init["schema"] = { - "name": "name_value", - "type_": 1, - "definition": "definition_value", - "revision_id": "revision_id_value", - "revision_create_time": {"seconds": 751, "nanos": 543}, - } - # The version of a generated dependency at test runtime may differ from the version used during generation. - # Delete any fields which are not present in the current runtime dependency - # See https://github.com/googleapis/gapic-generator-python/issues/1748 + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() - # Determine if the message type is proto-plus or protobuf - test_field = gp_schema.CreateSchemaRequest.meta.fields["schema"] + # Ensure method has been cached + assert client._transport.create_schema in client._transport._wrapped_methods - def get_message_fields(field): - # Given a field which is a message (composite type), return a list with - # all the fields of the message. - # If the field is not a composite type, return an empty list. - message_fields = [] + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[client._transport.create_schema] = mock_rpc - if hasattr(field, "message") and field.message: - is_field_type_proto_plus_type = not hasattr(field.message, "DESCRIPTOR") + request = {} + client.create_schema(request) - if is_field_type_proto_plus_type: - message_fields = field.message.meta.fields.values() - # Add `# pragma: NO COVER` because there may not be any `*_pb2` field types - else: # pragma: NO COVER - message_fields = field.message.DESCRIPTOR.fields - return message_fields + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 - runtime_nested_fields = [ - (field.name, nested_field.name) - for field in get_message_fields(test_field) - for nested_field in get_message_fields(field) - ] + client.create_schema(request) - subfields_not_in_runtime = [] - - # For each item in the sample request, create a list of sub fields which are not present at runtime - # Add `# pragma: NO COVER` because this test code will not run if all subfields are present at runtime - for field, value in request_init["schema"].items(): # pragma: NO COVER - result = None - is_repeated = False - # For repeated fields - if isinstance(value, list) and len(value): - is_repeated = True - result = value[0] - # For fields where the type is another message - if isinstance(value, dict): - result = value - - if result and hasattr(result, "keys"): - for subfield in result.keys(): - if (field, subfield) not in runtime_nested_fields: - subfields_not_in_runtime.append( - { - "field": field, - "subfield": subfield, - "is_repeated": is_repeated, - } - ) - - # Remove fields from the sample request which are not present in the runtime version of the dependency - # Add `# pragma: NO COVER` because this test code will not run if all subfields are present at runtime - for subfield_to_delete in subfields_not_in_runtime: # pragma: NO COVER - field = subfield_to_delete.get("field") - field_repeated = subfield_to_delete.get("is_repeated") - subfield = subfield_to_delete.get("subfield") - if subfield: - if field_repeated: - for i in range(0, len(request_init["schema"][field])): - del request_init["schema"][field][i][subfield] - else: - del request_init["schema"][field][subfield] - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = gp_schema.Schema( - name="name_value", - type_=gp_schema.Schema.Type.PROTOCOL_BUFFER, - definition="definition_value", - revision_id="revision_id_value", - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = gp_schema.Schema.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.create_schema(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, gp_schema.Schema) - assert response.name == "name_value" - assert response.type_ == gp_schema.Schema.Type.PROTOCOL_BUFFER - assert response.definition == "definition_value" - assert response.revision_id == "revision_id_value" - - -def test_create_schema_rest_use_cached_wrapped_rpc(): - # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, - # instead of constructing them on each call - with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # Should wrap all calls on client creation - assert wrapper_fn.call_count > 0 - wrapper_fn.reset_mock() - - # Ensure method has been cached - assert client._transport.create_schema in client._transport._wrapped_methods - - # Replace cached wrapped function with mock - mock_rpc = mock.Mock() - mock_rpc.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client._transport._wrapped_methods[client._transport.create_schema] = mock_rpc - - request = {} - client.create_schema(request) - - # Establish that the underlying gRPC stub method was called. - assert mock_rpc.call_count == 1 - - client.create_schema(request) - - # Establish that a new wrapper was not created for this call - assert wrapper_fn.call_count == 0 - assert mock_rpc.call_count == 2 + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 def test_create_schema_rest_required_fields(request_type=gp_schema.CreateSchemaRequest): @@ -5479,83 +4865,6 @@ def test_create_schema_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_create_schema_rest_interceptors(null_interceptor): - transport = transports.SchemaServiceRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SchemaServiceRestInterceptor(), - ) - client = SchemaServiceClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SchemaServiceRestInterceptor, "post_create_schema" - ) as post, mock.patch.object( - transports.SchemaServiceRestInterceptor, "pre_create_schema" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = gp_schema.CreateSchemaRequest.pb(gp_schema.CreateSchemaRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = gp_schema.Schema.to_json(gp_schema.Schema()) - - request = gp_schema.CreateSchemaRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = gp_schema.Schema() - - client.create_schema( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_create_schema_rest_bad_request( - transport: str = "rest", request_type=gp_schema.CreateSchemaRequest -): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"parent": "projects/sample1"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.create_schema(request) - - def test_create_schema_rest_flattened(): client = SchemaServiceClient( credentials=ga_credentials.AnonymousCredentials(), @@ -5615,58 +4924,6 @@ def test_create_schema_rest_flattened_error(transport: str = "rest"): ) -def test_create_schema_rest_error(): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - schema.GetSchemaRequest, - dict, - ], -) -def test_get_schema_rest(request_type): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/schemas/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = schema.Schema( - name="name_value", - type_=schema.Schema.Type.PROTOCOL_BUFFER, - definition="definition_value", - revision_id="revision_id_value", - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = schema.Schema.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.get_schema(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, schema.Schema) - assert response.name == "name_value" - assert response.type_ == schema.Schema.Type.PROTOCOL_BUFFER - assert response.definition == "definition_value" - assert response.revision_id == "revision_id_value" - - def test_get_schema_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -5786,96 +5043,19 @@ def test_get_schema_rest_unset_required_fields(): assert set(unset_fields) == (set(("view",)) & set(("name",))) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_get_schema_rest_interceptors(null_interceptor): - transport = transports.SchemaServiceRestTransport( +def test_get_schema_rest_flattened(): + client = SchemaServiceClient( credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SchemaServiceRestInterceptor(), + transport="rest", ) - client = SchemaServiceClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SchemaServiceRestInterceptor, "post_get_schema" - ) as post, mock.patch.object( - transports.SchemaServiceRestInterceptor, "pre_get_schema" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = schema.GetSchemaRequest.pb(schema.GetSchemaRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = schema.Schema.to_json(schema.Schema()) + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = schema.Schema() - request = schema.GetSchemaRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = schema.Schema() - - client.get_schema( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_get_schema_rest_bad_request( - transport: str = "rest", request_type=schema.GetSchemaRequest -): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/schemas/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.get_schema(request) - - -def test_get_schema_rest_flattened(): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = schema.Schema() - - # get arguments that satisfy an http rule for this method - sample_request = {"name": "projects/sample1/schemas/sample2"} + # get arguments that satisfy an http rule for this method + sample_request = {"name": "projects/sample1/schemas/sample2"} # get truthy value for each flattened field mock_args = dict( @@ -5918,52 +5098,6 @@ def test_get_schema_rest_flattened_error(transport: str = "rest"): ) -def test_get_schema_rest_error(): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - schema.ListSchemasRequest, - dict, - ], -) -def test_list_schemas_rest(request_type): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"parent": "projects/sample1"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = schema.ListSchemasResponse( - next_page_token="next_page_token_value", - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = schema.ListSchemasResponse.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.list_schemas(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pagers.ListSchemasPager) - assert response.next_page_token == "next_page_token_value" - - def test_list_schemas_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -6098,85 +5232,6 @@ def test_list_schemas_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_list_schemas_rest_interceptors(null_interceptor): - transport = transports.SchemaServiceRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SchemaServiceRestInterceptor(), - ) - client = SchemaServiceClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SchemaServiceRestInterceptor, "post_list_schemas" - ) as post, mock.patch.object( - transports.SchemaServiceRestInterceptor, "pre_list_schemas" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = schema.ListSchemasRequest.pb(schema.ListSchemasRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = schema.ListSchemasResponse.to_json( - schema.ListSchemasResponse() - ) - - request = schema.ListSchemasRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = schema.ListSchemasResponse() - - client.list_schemas( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_list_schemas_rest_bad_request( - transport: str = "rest", request_type=schema.ListSchemasRequest -): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"parent": "projects/sample1"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.list_schemas(request) - - def test_list_schemas_rest_flattened(): client = SchemaServiceClient( credentials=ga_credentials.AnonymousCredentials(), @@ -6293,46 +5348,6 @@ def test_list_schemas_rest_pager(transport: str = "rest"): assert page_.raw_page.next_page_token == token -@pytest.mark.parametrize( - "request_type", - [ - schema.ListSchemaRevisionsRequest, - dict, - ], -) -def test_list_schema_revisions_rest(request_type): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/schemas/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = schema.ListSchemaRevisionsResponse( - next_page_token="next_page_token_value", - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = schema.ListSchemaRevisionsResponse.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.list_schema_revisions(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pagers.ListSchemaRevisionsPager) - assert response.next_page_token == "next_page_token_value" - - def test_list_schema_revisions_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -6474,106 +5489,25 @@ def test_list_schema_revisions_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_list_schema_revisions_rest_interceptors(null_interceptor): - transport = transports.SchemaServiceRestTransport( +def test_list_schema_revisions_rest_flattened(): + client = SchemaServiceClient( credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SchemaServiceRestInterceptor(), + transport="rest", ) - client = SchemaServiceClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SchemaServiceRestInterceptor, "post_list_schema_revisions" - ) as post, mock.patch.object( - transports.SchemaServiceRestInterceptor, "pre_list_schema_revisions" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = schema.ListSchemaRevisionsRequest.pb( - schema.ListSchemaRevisionsRequest() - ) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = schema.ListSchemaRevisionsResponse.to_json( - schema.ListSchemaRevisionsResponse() - ) + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = schema.ListSchemaRevisionsResponse() - request = schema.ListSchemaRevisionsRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = schema.ListSchemaRevisionsResponse() + # get arguments that satisfy an http rule for this method + sample_request = {"name": "projects/sample1/schemas/sample2"} - client.list_schema_revisions( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], + # get truthy value for each flattened field + mock_args = dict( + name="name_value", ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_list_schema_revisions_rest_bad_request( - transport: str = "rest", request_type=schema.ListSchemaRevisionsRequest -): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/schemas/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.list_schema_revisions(request) - - -def test_list_schema_revisions_rest_flattened(): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = schema.ListSchemaRevisionsResponse() - - # get arguments that satisfy an http rule for this method - sample_request = {"name": "projects/sample1/schemas/sample2"} - - # get truthy value for each flattened field - mock_args = dict( - name="name_value", - ) - mock_args.update(sample_request) + mock_args.update(sample_request) # Wrap the value into a proper Response obj response_value = Response() @@ -6674,52 +5608,6 @@ def test_list_schema_revisions_rest_pager(transport: str = "rest"): assert page_.raw_page.next_page_token == token -@pytest.mark.parametrize( - "request_type", - [ - gp_schema.CommitSchemaRequest, - dict, - ], -) -def test_commit_schema_rest(request_type): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/schemas/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = gp_schema.Schema( - name="name_value", - type_=gp_schema.Schema.Type.PROTOCOL_BUFFER, - definition="definition_value", - revision_id="revision_id_value", - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = gp_schema.Schema.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.commit_schema(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, gp_schema.Schema) - assert response.name == "name_value" - assert response.type_ == gp_schema.Schema.Type.PROTOCOL_BUFFER - assert response.definition == "definition_value" - assert response.revision_id == "revision_id_value" - - def test_commit_schema_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -6846,83 +5734,6 @@ def test_commit_schema_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_commit_schema_rest_interceptors(null_interceptor): - transport = transports.SchemaServiceRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SchemaServiceRestInterceptor(), - ) - client = SchemaServiceClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SchemaServiceRestInterceptor, "post_commit_schema" - ) as post, mock.patch.object( - transports.SchemaServiceRestInterceptor, "pre_commit_schema" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = gp_schema.CommitSchemaRequest.pb(gp_schema.CommitSchemaRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = gp_schema.Schema.to_json(gp_schema.Schema()) - - request = gp_schema.CommitSchemaRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = gp_schema.Schema() - - client.commit_schema( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_commit_schema_rest_bad_request( - transport: str = "rest", request_type=gp_schema.CommitSchemaRequest -): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/schemas/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.commit_schema(request) - - def test_commit_schema_rest_flattened(): client = SchemaServiceClient( credentials=ga_credentials.AnonymousCredentials(), @@ -6980,58 +5791,6 @@ def test_commit_schema_rest_flattened_error(transport: str = "rest"): ) -def test_commit_schema_rest_error(): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - schema.RollbackSchemaRequest, - dict, - ], -) -def test_rollback_schema_rest(request_type): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/schemas/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = schema.Schema( - name="name_value", - type_=schema.Schema.Type.PROTOCOL_BUFFER, - definition="definition_value", - revision_id="revision_id_value", - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = schema.Schema.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.rollback_schema(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, schema.Schema) - assert response.name == "name_value" - assert response.type_ == schema.Schema.Type.PROTOCOL_BUFFER - assert response.definition == "definition_value" - assert response.revision_id == "revision_id_value" - - def test_rollback_schema_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -7164,96 +5923,19 @@ def test_rollback_schema_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_rollback_schema_rest_interceptors(null_interceptor): - transport = transports.SchemaServiceRestTransport( +def test_rollback_schema_rest_flattened(): + client = SchemaServiceClient( credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SchemaServiceRestInterceptor(), + transport="rest", ) - client = SchemaServiceClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SchemaServiceRestInterceptor, "post_rollback_schema" - ) as post, mock.patch.object( - transports.SchemaServiceRestInterceptor, "pre_rollback_schema" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = schema.RollbackSchemaRequest.pb(schema.RollbackSchemaRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = schema.Schema.to_json(schema.Schema()) + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = schema.Schema() - request = schema.RollbackSchemaRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = schema.Schema() - - client.rollback_schema( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_rollback_schema_rest_bad_request( - transport: str = "rest", request_type=schema.RollbackSchemaRequest -): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/schemas/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.rollback_schema(request) - - -def test_rollback_schema_rest_flattened(): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = schema.Schema() - - # get arguments that satisfy an http rule for this method - sample_request = {"name": "projects/sample1/schemas/sample2"} + # get arguments that satisfy an http rule for this method + sample_request = {"name": "projects/sample1/schemas/sample2"} # get truthy value for each flattened field mock_args = dict( @@ -7299,58 +5981,6 @@ def test_rollback_schema_rest_flattened_error(transport: str = "rest"): ) -def test_rollback_schema_rest_error(): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - schema.DeleteSchemaRevisionRequest, - dict, - ], -) -def test_delete_schema_revision_rest(request_type): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/schemas/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = schema.Schema( - name="name_value", - type_=schema.Schema.Type.PROTOCOL_BUFFER, - definition="definition_value", - revision_id="revision_id_value", - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = schema.Schema.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.delete_schema_revision(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, schema.Schema) - assert response.name == "name_value" - assert response.type_ == schema.Schema.Type.PROTOCOL_BUFFER - assert response.definition == "definition_value" - assert response.revision_id == "revision_id_value" - - def test_delete_schema_revision_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -7477,85 +6107,6 @@ def test_delete_schema_revision_rest_unset_required_fields(): assert set(unset_fields) == (set(("revisionId",)) & set(("name",))) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_delete_schema_revision_rest_interceptors(null_interceptor): - transport = transports.SchemaServiceRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SchemaServiceRestInterceptor(), - ) - client = SchemaServiceClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SchemaServiceRestInterceptor, "post_delete_schema_revision" - ) as post, mock.patch.object( - transports.SchemaServiceRestInterceptor, "pre_delete_schema_revision" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = schema.DeleteSchemaRevisionRequest.pb( - schema.DeleteSchemaRevisionRequest() - ) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = schema.Schema.to_json(schema.Schema()) - - request = schema.DeleteSchemaRevisionRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = schema.Schema() - - client.delete_schema_revision( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_delete_schema_revision_rest_bad_request( - transport: str = "rest", request_type=schema.DeleteSchemaRevisionRequest -): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/schemas/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.delete_schema_revision(request) - - def test_delete_schema_revision_rest_flattened(): client = SchemaServiceClient( credentials=ga_credentials.AnonymousCredentials(), @@ -7614,47 +6165,6 @@ def test_delete_schema_revision_rest_flattened_error(transport: str = "rest"): ) -def test_delete_schema_revision_rest_error(): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - schema.DeleteSchemaRequest, - dict, - ], -) -def test_delete_schema_rest(request_type): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/schemas/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = None - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = "" - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.delete_schema(request) - - # Establish that the response is the type that we expect. - assert response is None - - def test_delete_schema_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -7769,90 +6279,19 @@ def test_delete_schema_rest_unset_required_fields(): assert set(unset_fields) == (set(()) & set(("name",))) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_delete_schema_rest_interceptors(null_interceptor): - transport = transports.SchemaServiceRestTransport( +def test_delete_schema_rest_flattened(): + client = SchemaServiceClient( credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SchemaServiceRestInterceptor(), + transport="rest", ) - client = SchemaServiceClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SchemaServiceRestInterceptor, "pre_delete_schema" - ) as pre: - pre.assert_not_called() - pb_message = schema.DeleteSchemaRequest.pb(schema.DeleteSchemaRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = None - request = schema.DeleteSchemaRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - - client.delete_schema( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - - -def test_delete_schema_rest_bad_request( - transport: str = "rest", request_type=schema.DeleteSchemaRequest -): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/schemas/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.delete_schema(request) - - -def test_delete_schema_rest_flattened(): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = None - - # get arguments that satisfy an http rule for this method - sample_request = {"name": "projects/sample1/schemas/sample2"} + # get arguments that satisfy an http rule for this method + sample_request = {"name": "projects/sample1/schemas/sample2"} # get truthy value for each flattened field mock_args = dict( @@ -7893,49 +6332,6 @@ def test_delete_schema_rest_flattened_error(transport: str = "rest"): ) -def test_delete_schema_rest_error(): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - gp_schema.ValidateSchemaRequest, - dict, - ], -) -def test_validate_schema_rest(request_type): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"parent": "projects/sample1"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = gp_schema.ValidateSchemaResponse() - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = gp_schema.ValidateSchemaResponse.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.validate_schema(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, gp_schema.ValidateSchemaResponse) - - def test_validate_schema_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -8064,87 +6460,6 @@ def test_validate_schema_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_validate_schema_rest_interceptors(null_interceptor): - transport = transports.SchemaServiceRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SchemaServiceRestInterceptor(), - ) - client = SchemaServiceClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SchemaServiceRestInterceptor, "post_validate_schema" - ) as post, mock.patch.object( - transports.SchemaServiceRestInterceptor, "pre_validate_schema" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = gp_schema.ValidateSchemaRequest.pb( - gp_schema.ValidateSchemaRequest() - ) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = gp_schema.ValidateSchemaResponse.to_json( - gp_schema.ValidateSchemaResponse() - ) - - request = gp_schema.ValidateSchemaRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = gp_schema.ValidateSchemaResponse() - - client.validate_schema( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_validate_schema_rest_bad_request( - transport: str = "rest", request_type=gp_schema.ValidateSchemaRequest -): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"parent": "projects/sample1"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.validate_schema(request) - - def test_validate_schema_rest_flattened(): client = SchemaServiceClient( credentials=ga_credentials.AnonymousCredentials(), @@ -8203,49 +6518,6 @@ def test_validate_schema_rest_flattened_error(transport: str = "rest"): ) -def test_validate_schema_rest_error(): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - schema.ValidateMessageRequest, - dict, - ], -) -def test_validate_message_rest(request_type): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"parent": "projects/sample1"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = schema.ValidateMessageResponse() - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = schema.ValidateMessageResponse.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.validate_message(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, schema.ValidateMessageResponse) - - def test_validate_message_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -8368,110 +6640,25 @@ def test_validate_message_rest_unset_required_fields(): assert set(unset_fields) == (set(()) & set(("parent",))) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_validate_message_rest_interceptors(null_interceptor): - transport = transports.SchemaServiceRestTransport( +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.SchemaServiceGrpcTransport( credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SchemaServiceRestInterceptor(), ) - client = SchemaServiceClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SchemaServiceRestInterceptor, "post_validate_message" - ) as post, mock.patch.object( - transports.SchemaServiceRestInterceptor, "pre_validate_message" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = schema.ValidateMessageRequest.pb(schema.ValidateMessageRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = schema.ValidateMessageResponse.to_json( - schema.ValidateMessageResponse() + with pytest.raises(ValueError): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, ) - request = schema.ValidateMessageRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = schema.ValidateMessageResponse() - - client.validate_message( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_validate_message_rest_bad_request( - transport: str = "rest", request_type=schema.ValidateMessageRequest -): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"parent": "projects/sample1"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.validate_message(request) - - -def test_validate_message_rest_error(): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -def test_credentials_transport_error(): - # It is an error to provide credentials and a transport instance. - transport = transports.SchemaServiceGrpcTransport( - credentials=ga_credentials.AnonymousCredentials(), - ) - with pytest.raises(ValueError): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # It is an error to provide a credentials file and a transport instance. - transport = transports.SchemaServiceGrpcTransport( - credentials=ga_credentials.AnonymousCredentials(), - ) - with pytest.raises(ValueError): - client = SchemaServiceClient( - client_options={"credentials_file": "credentials.json"}, - transport=transport, + # It is an error to provide a credentials file and a transport instance. + transport = transports.SchemaServiceGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = SchemaServiceClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, ) # It is an error to provide an api_key and a transport instance. @@ -8529,34 +6716,2192 @@ def test_transport_get_channel(): assert channel -@pytest.mark.parametrize( - "transport_class", - [ - transports.SchemaServiceGrpcTransport, - transports.SchemaServiceGrpcAsyncIOTransport, - transports.SchemaServiceRestTransport, - ], -) -def test_transport_adc(transport_class): - # Test default credentials are used if not provided. - with mock.patch.object(google.auth, "default") as adc: - adc.return_value = (ga_credentials.AnonymousCredentials(), None) - transport_class() - adc.assert_called_once() +@pytest.mark.parametrize( + "transport_class", + [ + transports.SchemaServiceGrpcTransport, + transports.SchemaServiceGrpcAsyncIOTransport, + transports.SchemaServiceRestTransport, + ], +) +def test_transport_adc(transport_class): + # Test default credentials are used if not provided. + with mock.patch.object(google.auth, "default") as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class() + adc.assert_called_once() + + +def test_transport_kind_grpc(): + transport = SchemaServiceClient.get_transport_class("grpc")( + credentials=ga_credentials.AnonymousCredentials() + ) + assert transport.kind == "grpc" + + +def test_initialize_client_w_grpc(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc" + ) + assert client is not None + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_create_schema_empty_call_grpc(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.create_schema), "__call__") as call: + call.return_value = gp_schema.Schema() + client.create_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = gp_schema.CreateSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_get_schema_empty_call_grpc(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.get_schema), "__call__") as call: + call.return_value = schema.Schema() + client.get_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.GetSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_list_schemas_empty_call_grpc(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.list_schemas), "__call__") as call: + call.return_value = schema.ListSchemasResponse() + client.list_schemas(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.ListSchemasRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_list_schema_revisions_empty_call_grpc(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.list_schema_revisions), "__call__" + ) as call: + call.return_value = schema.ListSchemaRevisionsResponse() + client.list_schema_revisions(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.ListSchemaRevisionsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_commit_schema_empty_call_grpc(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.commit_schema), "__call__") as call: + call.return_value = gp_schema.Schema() + client.commit_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = gp_schema.CommitSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_rollback_schema_empty_call_grpc(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.rollback_schema), "__call__") as call: + call.return_value = schema.Schema() + client.rollback_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.RollbackSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_delete_schema_revision_empty_call_grpc(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.delete_schema_revision), "__call__" + ) as call: + call.return_value = schema.Schema() + client.delete_schema_revision(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.DeleteSchemaRevisionRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_delete_schema_empty_call_grpc(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.delete_schema), "__call__") as call: + call.return_value = None + client.delete_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.DeleteSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_validate_schema_empty_call_grpc(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.validate_schema), "__call__") as call: + call.return_value = gp_schema.ValidateSchemaResponse() + client.validate_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = gp_schema.ValidateSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_validate_message_empty_call_grpc(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.validate_message), "__call__") as call: + call.return_value = schema.ValidateMessageResponse() + client.validate_message(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.ValidateMessageRequest() + + assert args[0] == request_msg + + +def test_transport_kind_grpc_asyncio(): + transport = SchemaServiceAsyncClient.get_transport_class("grpc_asyncio")( + credentials=async_anonymous_credentials() + ) + assert transport.kind == "grpc_asyncio" + + +def test_initialize_client_w_grpc_asyncio(): + client = SchemaServiceAsyncClient( + credentials=async_anonymous_credentials(), transport="grpc_asyncio" + ) + assert client is not None + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_create_schema_empty_call_grpc_asyncio(): + client = SchemaServiceAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.create_schema), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gp_schema.Schema( + name="name_value", + type_=gp_schema.Schema.Type.PROTOCOL_BUFFER, + definition="definition_value", + revision_id="revision_id_value", + ) + ) + await client.create_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = gp_schema.CreateSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_get_schema_empty_call_grpc_asyncio(): + client = SchemaServiceAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.get_schema), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + schema.Schema( + name="name_value", + type_=schema.Schema.Type.PROTOCOL_BUFFER, + definition="definition_value", + revision_id="revision_id_value", + ) + ) + await client.get_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.GetSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_list_schemas_empty_call_grpc_asyncio(): + client = SchemaServiceAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.list_schemas), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + schema.ListSchemasResponse( + next_page_token="next_page_token_value", + ) + ) + await client.list_schemas(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.ListSchemasRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_list_schema_revisions_empty_call_grpc_asyncio(): + client = SchemaServiceAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.list_schema_revisions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + schema.ListSchemaRevisionsResponse( + next_page_token="next_page_token_value", + ) + ) + await client.list_schema_revisions(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.ListSchemaRevisionsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_commit_schema_empty_call_grpc_asyncio(): + client = SchemaServiceAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.commit_schema), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gp_schema.Schema( + name="name_value", + type_=gp_schema.Schema.Type.PROTOCOL_BUFFER, + definition="definition_value", + revision_id="revision_id_value", + ) + ) + await client.commit_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = gp_schema.CommitSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_rollback_schema_empty_call_grpc_asyncio(): + client = SchemaServiceAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.rollback_schema), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + schema.Schema( + name="name_value", + type_=schema.Schema.Type.PROTOCOL_BUFFER, + definition="definition_value", + revision_id="revision_id_value", + ) + ) + await client.rollback_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.RollbackSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_delete_schema_revision_empty_call_grpc_asyncio(): + client = SchemaServiceAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.delete_schema_revision), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + schema.Schema( + name="name_value", + type_=schema.Schema.Type.PROTOCOL_BUFFER, + definition="definition_value", + revision_id="revision_id_value", + ) + ) + await client.delete_schema_revision(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.DeleteSchemaRevisionRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_delete_schema_empty_call_grpc_asyncio(): + client = SchemaServiceAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.delete_schema), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + await client.delete_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.DeleteSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_validate_schema_empty_call_grpc_asyncio(): + client = SchemaServiceAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.validate_schema), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gp_schema.ValidateSchemaResponse() + ) + await client.validate_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = gp_schema.ValidateSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_validate_message_empty_call_grpc_asyncio(): + client = SchemaServiceAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.validate_message), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + schema.ValidateMessageResponse() + ) + await client.validate_message(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.ValidateMessageRequest() + + assert args[0] == request_msg + + +def test_transport_kind_rest(): + transport = SchemaServiceClient.get_transport_class("rest")( + credentials=ga_credentials.AnonymousCredentials() + ) + assert transport.kind == "rest" + + +def test_create_schema_rest_bad_request(request_type=gp_schema.CreateSchemaRequest): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"parent": "projects/sample1"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.create_schema(request) + + +@pytest.mark.parametrize( + "request_type", + [ + gp_schema.CreateSchemaRequest, + dict, + ], +) +def test_create_schema_rest_call_success(request_type): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"parent": "projects/sample1"} + request_init["schema"] = { + "name": "name_value", + "type_": 1, + "definition": "definition_value", + "revision_id": "revision_id_value", + "revision_create_time": {"seconds": 751, "nanos": 543}, + } + # The version of a generated dependency at test runtime may differ from the version used during generation. + # Delete any fields which are not present in the current runtime dependency + # See https://github.com/googleapis/gapic-generator-python/issues/1748 + + # Determine if the message type is proto-plus or protobuf + test_field = gp_schema.CreateSchemaRequest.meta.fields["schema"] + + def get_message_fields(field): + # Given a field which is a message (composite type), return a list with + # all the fields of the message. + # If the field is not a composite type, return an empty list. + message_fields = [] + + if hasattr(field, "message") and field.message: + is_field_type_proto_plus_type = not hasattr(field.message, "DESCRIPTOR") + + if is_field_type_proto_plus_type: + message_fields = field.message.meta.fields.values() + # Add `# pragma: NO COVER` because there may not be any `*_pb2` field types + else: # pragma: NO COVER + message_fields = field.message.DESCRIPTOR.fields + return message_fields + + runtime_nested_fields = [ + (field.name, nested_field.name) + for field in get_message_fields(test_field) + for nested_field in get_message_fields(field) + ] + + subfields_not_in_runtime = [] + + # For each item in the sample request, create a list of sub fields which are not present at runtime + # Add `# pragma: NO COVER` because this test code will not run if all subfields are present at runtime + for field, value in request_init["schema"].items(): # pragma: NO COVER + result = None + is_repeated = False + # For repeated fields + if isinstance(value, list) and len(value): + is_repeated = True + result = value[0] + # For fields where the type is another message + if isinstance(value, dict): + result = value + + if result and hasattr(result, "keys"): + for subfield in result.keys(): + if (field, subfield) not in runtime_nested_fields: + subfields_not_in_runtime.append( + { + "field": field, + "subfield": subfield, + "is_repeated": is_repeated, + } + ) + + # Remove fields from the sample request which are not present in the runtime version of the dependency + # Add `# pragma: NO COVER` because this test code will not run if all subfields are present at runtime + for subfield_to_delete in subfields_not_in_runtime: # pragma: NO COVER + field = subfield_to_delete.get("field") + field_repeated = subfield_to_delete.get("is_repeated") + subfield = subfield_to_delete.get("subfield") + if subfield: + if field_repeated: + for i in range(0, len(request_init["schema"][field])): + del request_init["schema"][field][i][subfield] + else: + del request_init["schema"][field][subfield] + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = gp_schema.Schema( + name="name_value", + type_=gp_schema.Schema.Type.PROTOCOL_BUFFER, + definition="definition_value", + revision_id="revision_id_value", + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = gp_schema.Schema.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.create_schema(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, gp_schema.Schema) + assert response.name == "name_value" + assert response.type_ == gp_schema.Schema.Type.PROTOCOL_BUFFER + assert response.definition == "definition_value" + assert response.revision_id == "revision_id_value" + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_create_schema_rest_interceptors(null_interceptor): + transport = transports.SchemaServiceRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SchemaServiceRestInterceptor(), + ) + client = SchemaServiceClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_create_schema" + ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "pre_create_schema" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = gp_schema.CreateSchemaRequest.pb(gp_schema.CreateSchemaRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = gp_schema.Schema.to_json(gp_schema.Schema()) + req.return_value.content = return_value + + request = gp_schema.CreateSchemaRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = gp_schema.Schema() + + client.create_schema( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_get_schema_rest_bad_request(request_type=schema.GetSchemaRequest): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/schemas/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.get_schema(request) + + +@pytest.mark.parametrize( + "request_type", + [ + schema.GetSchemaRequest, + dict, + ], +) +def test_get_schema_rest_call_success(request_type): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/schemas/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = schema.Schema( + name="name_value", + type_=schema.Schema.Type.PROTOCOL_BUFFER, + definition="definition_value", + revision_id="revision_id_value", + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = schema.Schema.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.get_schema(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, schema.Schema) + assert response.name == "name_value" + assert response.type_ == schema.Schema.Type.PROTOCOL_BUFFER + assert response.definition == "definition_value" + assert response.revision_id == "revision_id_value" + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_get_schema_rest_interceptors(null_interceptor): + transport = transports.SchemaServiceRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SchemaServiceRestInterceptor(), + ) + client = SchemaServiceClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_get_schema" + ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "pre_get_schema" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = schema.GetSchemaRequest.pb(schema.GetSchemaRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = schema.Schema.to_json(schema.Schema()) + req.return_value.content = return_value + + request = schema.GetSchemaRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = schema.Schema() + + client.get_schema( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_list_schemas_rest_bad_request(request_type=schema.ListSchemasRequest): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"parent": "projects/sample1"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.list_schemas(request) + + +@pytest.mark.parametrize( + "request_type", + [ + schema.ListSchemasRequest, + dict, + ], +) +def test_list_schemas_rest_call_success(request_type): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"parent": "projects/sample1"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = schema.ListSchemasResponse( + next_page_token="next_page_token_value", + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = schema.ListSchemasResponse.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.list_schemas(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListSchemasPager) + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_list_schemas_rest_interceptors(null_interceptor): + transport = transports.SchemaServiceRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SchemaServiceRestInterceptor(), + ) + client = SchemaServiceClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_list_schemas" + ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "pre_list_schemas" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = schema.ListSchemasRequest.pb(schema.ListSchemasRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = schema.ListSchemasResponse.to_json(schema.ListSchemasResponse()) + req.return_value.content = return_value + + request = schema.ListSchemasRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = schema.ListSchemasResponse() + + client.list_schemas( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_list_schema_revisions_rest_bad_request( + request_type=schema.ListSchemaRevisionsRequest, +): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/schemas/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.list_schema_revisions(request) + + +@pytest.mark.parametrize( + "request_type", + [ + schema.ListSchemaRevisionsRequest, + dict, + ], +) +def test_list_schema_revisions_rest_call_success(request_type): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/schemas/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = schema.ListSchemaRevisionsResponse( + next_page_token="next_page_token_value", + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = schema.ListSchemaRevisionsResponse.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.list_schema_revisions(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListSchemaRevisionsPager) + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_list_schema_revisions_rest_interceptors(null_interceptor): + transport = transports.SchemaServiceRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SchemaServiceRestInterceptor(), + ) + client = SchemaServiceClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_list_schema_revisions" + ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "pre_list_schema_revisions" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = schema.ListSchemaRevisionsRequest.pb( + schema.ListSchemaRevisionsRequest() + ) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = schema.ListSchemaRevisionsResponse.to_json( + schema.ListSchemaRevisionsResponse() + ) + req.return_value.content = return_value + + request = schema.ListSchemaRevisionsRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = schema.ListSchemaRevisionsResponse() + + client.list_schema_revisions( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_commit_schema_rest_bad_request(request_type=gp_schema.CommitSchemaRequest): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/schemas/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.commit_schema(request) + + +@pytest.mark.parametrize( + "request_type", + [ + gp_schema.CommitSchemaRequest, + dict, + ], +) +def test_commit_schema_rest_call_success(request_type): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/schemas/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = gp_schema.Schema( + name="name_value", + type_=gp_schema.Schema.Type.PROTOCOL_BUFFER, + definition="definition_value", + revision_id="revision_id_value", + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = gp_schema.Schema.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.commit_schema(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, gp_schema.Schema) + assert response.name == "name_value" + assert response.type_ == gp_schema.Schema.Type.PROTOCOL_BUFFER + assert response.definition == "definition_value" + assert response.revision_id == "revision_id_value" + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_commit_schema_rest_interceptors(null_interceptor): + transport = transports.SchemaServiceRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SchemaServiceRestInterceptor(), + ) + client = SchemaServiceClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_commit_schema" + ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "pre_commit_schema" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = gp_schema.CommitSchemaRequest.pb(gp_schema.CommitSchemaRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = gp_schema.Schema.to_json(gp_schema.Schema()) + req.return_value.content = return_value + + request = gp_schema.CommitSchemaRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = gp_schema.Schema() + + client.commit_schema( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_rollback_schema_rest_bad_request(request_type=schema.RollbackSchemaRequest): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/schemas/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.rollback_schema(request) + + +@pytest.mark.parametrize( + "request_type", + [ + schema.RollbackSchemaRequest, + dict, + ], +) +def test_rollback_schema_rest_call_success(request_type): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/schemas/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = schema.Schema( + name="name_value", + type_=schema.Schema.Type.PROTOCOL_BUFFER, + definition="definition_value", + revision_id="revision_id_value", + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = schema.Schema.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.rollback_schema(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, schema.Schema) + assert response.name == "name_value" + assert response.type_ == schema.Schema.Type.PROTOCOL_BUFFER + assert response.definition == "definition_value" + assert response.revision_id == "revision_id_value" + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_rollback_schema_rest_interceptors(null_interceptor): + transport = transports.SchemaServiceRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SchemaServiceRestInterceptor(), + ) + client = SchemaServiceClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_rollback_schema" + ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "pre_rollback_schema" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = schema.RollbackSchemaRequest.pb(schema.RollbackSchemaRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = schema.Schema.to_json(schema.Schema()) + req.return_value.content = return_value + + request = schema.RollbackSchemaRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = schema.Schema() + + client.rollback_schema( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_delete_schema_revision_rest_bad_request( + request_type=schema.DeleteSchemaRevisionRequest, +): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/schemas/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.delete_schema_revision(request) + + +@pytest.mark.parametrize( + "request_type", + [ + schema.DeleteSchemaRevisionRequest, + dict, + ], +) +def test_delete_schema_revision_rest_call_success(request_type): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/schemas/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = schema.Schema( + name="name_value", + type_=schema.Schema.Type.PROTOCOL_BUFFER, + definition="definition_value", + revision_id="revision_id_value", + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = schema.Schema.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.delete_schema_revision(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, schema.Schema) + assert response.name == "name_value" + assert response.type_ == schema.Schema.Type.PROTOCOL_BUFFER + assert response.definition == "definition_value" + assert response.revision_id == "revision_id_value" + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_delete_schema_revision_rest_interceptors(null_interceptor): + transport = transports.SchemaServiceRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SchemaServiceRestInterceptor(), + ) + client = SchemaServiceClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_delete_schema_revision" + ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "pre_delete_schema_revision" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = schema.DeleteSchemaRevisionRequest.pb( + schema.DeleteSchemaRevisionRequest() + ) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = schema.Schema.to_json(schema.Schema()) + req.return_value.content = return_value + + request = schema.DeleteSchemaRevisionRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = schema.Schema() + + client.delete_schema_revision( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_delete_schema_rest_bad_request(request_type=schema.DeleteSchemaRequest): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/schemas/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.delete_schema(request) + + +@pytest.mark.parametrize( + "request_type", + [ + schema.DeleteSchemaRequest, + dict, + ], +) +def test_delete_schema_rest_call_success(request_type): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/schemas/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = None + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = "" + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.delete_schema(request) + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_delete_schema_rest_interceptors(null_interceptor): + transport = transports.SchemaServiceRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SchemaServiceRestInterceptor(), + ) + client = SchemaServiceClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SchemaServiceRestInterceptor, "pre_delete_schema" + ) as pre: + pre.assert_not_called() + pb_message = schema.DeleteSchemaRequest.pb(schema.DeleteSchemaRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + + request = schema.DeleteSchemaRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + + client.delete_schema( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + + +def test_validate_schema_rest_bad_request(request_type=gp_schema.ValidateSchemaRequest): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"parent": "projects/sample1"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.validate_schema(request) + + +@pytest.mark.parametrize( + "request_type", + [ + gp_schema.ValidateSchemaRequest, + dict, + ], +) +def test_validate_schema_rest_call_success(request_type): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"parent": "projects/sample1"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = gp_schema.ValidateSchemaResponse() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = gp_schema.ValidateSchemaResponse.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.validate_schema(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, gp_schema.ValidateSchemaResponse) + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_validate_schema_rest_interceptors(null_interceptor): + transport = transports.SchemaServiceRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SchemaServiceRestInterceptor(), + ) + client = SchemaServiceClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_validate_schema" + ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "pre_validate_schema" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = gp_schema.ValidateSchemaRequest.pb( + gp_schema.ValidateSchemaRequest() + ) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = gp_schema.ValidateSchemaResponse.to_json( + gp_schema.ValidateSchemaResponse() + ) + req.return_value.content = return_value + + request = gp_schema.ValidateSchemaRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = gp_schema.ValidateSchemaResponse() + + client.validate_schema( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_validate_message_rest_bad_request(request_type=schema.ValidateMessageRequest): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"parent": "projects/sample1"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.validate_message(request) + + +@pytest.mark.parametrize( + "request_type", + [ + schema.ValidateMessageRequest, + dict, + ], +) +def test_validate_message_rest_call_success(request_type): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"parent": "projects/sample1"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = schema.ValidateMessageResponse() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = schema.ValidateMessageResponse.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.validate_message(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, schema.ValidateMessageResponse) + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_validate_message_rest_interceptors(null_interceptor): + transport = transports.SchemaServiceRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SchemaServiceRestInterceptor(), + ) + client = SchemaServiceClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_validate_message" + ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "pre_validate_message" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = schema.ValidateMessageRequest.pb(schema.ValidateMessageRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = schema.ValidateMessageResponse.to_json( + schema.ValidateMessageResponse() + ) + req.return_value.content = return_value + + request = schema.ValidateMessageRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = schema.ValidateMessageResponse() + + client.validate_message( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_get_iam_policy_rest_bad_request( + request_type=iam_policy_pb2.GetIamPolicyRequest, +): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type() + request = json_format.ParseDict( + {"resource": "projects/sample1/topics/sample2"}, request + ) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = Response() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + client.get_iam_policy(request) + + +@pytest.mark.parametrize( + "request_type", + [ + iam_policy_pb2.GetIamPolicyRequest, + dict, + ], +) +def test_get_iam_policy_rest(request_type): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + request_init = {"resource": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "request") as req: + # Designate an appropriate value for the returned response. + return_value = policy_pb2.Policy() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + + req.return_value = response_value + + response = client.get_iam_policy(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + + +def test_set_iam_policy_rest_bad_request( + request_type=iam_policy_pb2.SetIamPolicyRequest, +): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type() + request = json_format.ParseDict( + {"resource": "projects/sample1/topics/sample2"}, request + ) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = Response() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + client.set_iam_policy(request) + + +@pytest.mark.parametrize( + "request_type", + [ + iam_policy_pb2.SetIamPolicyRequest, + dict, + ], +) +def test_set_iam_policy_rest(request_type): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + request_init = {"resource": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "request") as req: + # Designate an appropriate value for the returned response. + return_value = policy_pb2.Policy() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + + req.return_value = response_value + + response = client.set_iam_policy(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + + +def test_test_iam_permissions_rest_bad_request( + request_type=iam_policy_pb2.TestIamPermissionsRequest, +): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type() + request = json_format.ParseDict( + {"resource": "projects/sample1/subscriptions/sample2"}, request + ) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = Response() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + client.test_iam_permissions(request) + + +@pytest.mark.parametrize( + "request_type", + [ + iam_policy_pb2.TestIamPermissionsRequest, + dict, + ], +) +def test_test_iam_permissions_rest(request_type): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + request_init = {"resource": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "request") as req: + # Designate an appropriate value for the returned response. + return_value = iam_policy_pb2.TestIamPermissionsResponse() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + + req.return_value = response_value + + response = client.test_iam_permissions(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) + + +def test_initialize_client_w_rest(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + assert client is not None + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_create_schema_empty_call_rest(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.create_schema), "__call__") as call: + client.create_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = gp_schema.CreateSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_get_schema_empty_call_rest(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.get_schema), "__call__") as call: + client.get_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.GetSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_list_schemas_empty_call_rest(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.list_schemas), "__call__") as call: + client.list_schemas(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.ListSchemasRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_list_schema_revisions_empty_call_rest(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.list_schema_revisions), "__call__" + ) as call: + client.list_schema_revisions(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.ListSchemaRevisionsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_commit_schema_empty_call_rest(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.commit_schema), "__call__") as call: + client.commit_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = gp_schema.CommitSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_rollback_schema_empty_call_rest(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.rollback_schema), "__call__") as call: + client.rollback_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.RollbackSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_delete_schema_revision_empty_call_rest(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.delete_schema_revision), "__call__" + ) as call: + client.delete_schema_revision(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.DeleteSchemaRevisionRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_delete_schema_empty_call_rest(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.delete_schema), "__call__") as call: + client.delete_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.DeleteSchemaRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_validate_schema_empty_call_rest(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.validate_schema), "__call__") as call: + client.validate_schema(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = gp_schema.ValidateSchemaRequest() + assert args[0] == request_msg -@pytest.mark.parametrize( - "transport_name", - [ - "grpc", - "rest", - ], -) -def test_transport_kind(transport_name): - transport = SchemaServiceClient.get_transport_class(transport_name)( + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_validate_message_empty_call_rest(): + client = SchemaServiceClient( credentials=ga_credentials.AnonymousCredentials(), + transport="rest", ) - assert transport.kind == transport_name + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.validate_message), "__call__") as call: + client.validate_message(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = schema.ValidateMessageRequest() + + assert args[0] == request_msg def test_transport_grpc_default(): @@ -9188,194 +9533,6 @@ def test_client_with_default_client_info(): prep.assert_called_once_with(client_info) -@pytest.mark.asyncio -async def test_transport_close_async(): - client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - with mock.patch.object( - type(getattr(client.transport, "grpc_channel")), "close" - ) as close: - async with client: - close.assert_not_called() - close.assert_called_once() - - -def test_get_iam_policy_rest_bad_request( - transport: str = "rest", request_type=iam_policy_pb2.GetIamPolicyRequest -): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - request = request_type() - request = json_format.ParseDict( - {"resource": "projects/sample1/topics/sample2"}, request - ) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.get_iam_policy(request) - - -@pytest.mark.parametrize( - "request_type", - [ - iam_policy_pb2.GetIamPolicyRequest, - dict, - ], -) -def test_get_iam_policy_rest(request_type): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - request_init = {"resource": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = policy_pb2.Policy() - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - - response = client.get_iam_policy(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, policy_pb2.Policy) - - -def test_set_iam_policy_rest_bad_request( - transport: str = "rest", request_type=iam_policy_pb2.SetIamPolicyRequest -): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - request = request_type() - request = json_format.ParseDict( - {"resource": "projects/sample1/topics/sample2"}, request - ) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.set_iam_policy(request) - - -@pytest.mark.parametrize( - "request_type", - [ - iam_policy_pb2.SetIamPolicyRequest, - dict, - ], -) -def test_set_iam_policy_rest(request_type): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - request_init = {"resource": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = policy_pb2.Policy() - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - - response = client.set_iam_policy(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, policy_pb2.Policy) - - -def test_test_iam_permissions_rest_bad_request( - transport: str = "rest", request_type=iam_policy_pb2.TestIamPermissionsRequest -): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - request = request_type() - request = json_format.ParseDict( - {"resource": "projects/sample1/subscriptions/sample2"}, request - ) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.test_iam_permissions(request) - - -@pytest.mark.parametrize( - "request_type", - [ - iam_policy_pb2.TestIamPermissionsRequest, - dict, - ], -) -def test_test_iam_permissions_rest(request_type): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - request_init = {"resource": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = iam_policy_pb2.TestIamPermissionsResponse() - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - - response = client.test_iam_permissions(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) - - def test_set_iam_policy(transport: str = "grpc"): client = SchemaServiceClient( credentials=ga_credentials.AnonymousCredentials(), @@ -9411,7 +9568,7 @@ def test_set_iam_policy(transport: str = "grpc"): @pytest.mark.asyncio async def test_set_iam_policy_async(transport: str = "grpc_asyncio"): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -9476,7 +9633,7 @@ def test_set_iam_policy_field_headers(): @pytest.mark.asyncio async def test_set_iam_policy_field_headers_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -9524,7 +9681,7 @@ def test_set_iam_policy_from_dict(): @pytest.mark.asyncio async def test_set_iam_policy_from_dict_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: @@ -9577,7 +9734,7 @@ def test_get_iam_policy(transport: str = "grpc"): @pytest.mark.asyncio async def test_get_iam_policy_async(transport: str = "grpc_asyncio"): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -9643,7 +9800,7 @@ def test_get_iam_policy_field_headers(): @pytest.mark.asyncio async def test_get_iam_policy_field_headers_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -9691,7 +9848,7 @@ def test_get_iam_policy_from_dict(): @pytest.mark.asyncio async def test_get_iam_policy_from_dict_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: @@ -9743,7 +9900,7 @@ def test_test_iam_permissions(transport: str = "grpc"): @pytest.mark.asyncio async def test_test_iam_permissions_async(transport: str = "grpc_asyncio"): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -9810,7 +9967,7 @@ def test_test_iam_permissions_field_headers(): @pytest.mark.asyncio async def test_test_iam_permissions_field_headers_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -9864,7 +10021,7 @@ def test_test_iam_permissions_from_dict(): @pytest.mark.asyncio async def test_test_iam_permissions_from_dict_async(): client = SchemaServiceAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( @@ -9884,22 +10041,41 @@ async def test_test_iam_permissions_from_dict_async(): call.assert_called() -def test_transport_close(): - transports = { - "rest": "_session", - "grpc": "_grpc_channel", - } +def test_transport_close_grpc(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc" + ) + with mock.patch.object( + type(getattr(client.transport, "_grpc_channel")), "close" + ) as close: + with client: + close.assert_not_called() + close.assert_called_once() - for transport, close_name in transports.items(): - client = SchemaServiceClient( - credentials=ga_credentials.AnonymousCredentials(), transport=transport - ) - with mock.patch.object( - type(getattr(client.transport, close_name)), "close" - ) as close: - with client: - close.assert_not_called() - close.assert_called_once() + +@pytest.mark.asyncio +async def test_transport_close_grpc_asyncio(): + client = SchemaServiceAsyncClient( + credentials=async_anonymous_credentials(), transport="grpc_asyncio" + ) + with mock.patch.object( + type(getattr(client.transport, "_grpc_channel")), "close" + ) as close: + async with client: + close.assert_not_called() + close.assert_called_once() + + +def test_transport_close_rest(): + client = SchemaServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + with mock.patch.object( + type(getattr(client.transport, "_session")), "close" + ) as close: + with client: + close.assert_not_called() + close.assert_called_once() def test_client_ctx(): diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py index 4bef862ec..4478942f7 100644 --- a/tests/unit/gapic/pubsub_v1/test_subscriber.py +++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py @@ -25,7 +25,7 @@ import grpc from grpc.experimental import aio -from collections.abc import Iterable +from collections.abc import Iterable, AsyncIterable from google.protobuf import json_format import json import math @@ -38,6 +38,13 @@ from requests.sessions import Session from google.protobuf import json_format +try: + from google.auth.aio import credentials as ga_credentials_async + + HAS_GOOGLE_AUTH_AIO = True +except ImportError: # pragma: NO COVER + HAS_GOOGLE_AUTH_AIO = False + from google.api_core import client_options from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 @@ -62,10 +69,24 @@ import google.auth +async def mock_async_gen(data, chunk_size=1): + for i in range(0, len(data)): # pragma: NO COVER + chunk = data[i : i + chunk_size] + yield chunk.encode("utf-8") + + def client_cert_source_callback(): return b"cert bytes", b"key bytes" +# TODO: use async auth anon credentials by default once the minimum version of google-auth is upgraded. +# See related issue: https://github.com/googleapis/gapic-generator-python/issues/2107. +def async_anonymous_credentials(): + if HAS_GOOGLE_AUTH_AIO: + return ga_credentials_async.AnonymousCredentials() + return ga_credentials.AnonymousCredentials() + + # If default endpoint is localhost, then default mtls endpoint will be the same. # This method modifies the default endpoint so the client can produce a different # mtls endpoint for endpoint testing purposes. @@ -282,86 +303,6 @@ def test__get_universe_domain(): assert str(excinfo.value) == "Universe Domain cannot be an empty string." -@pytest.mark.parametrize( - "client_class,transport_class,transport_name", - [ - (SubscriberClient, transports.SubscriberGrpcTransport, "grpc"), - (SubscriberClient, transports.SubscriberRestTransport, "rest"), - ], -) -def test__validate_universe_domain(client_class, transport_class, transport_name): - client = client_class( - transport=transport_class(credentials=ga_credentials.AnonymousCredentials()) - ) - assert client._validate_universe_domain() == True - - # Test the case when universe is already validated. - assert client._validate_universe_domain() == True - - if transport_name == "grpc": - # Test the case where credentials are provided by the - # `local_channel_credentials`. The default universes in both match. - channel = grpc.secure_channel( - "http://localhost/", grpc.local_channel_credentials() - ) - client = client_class(transport=transport_class(channel=channel)) - assert client._validate_universe_domain() == True - - # Test the case where credentials do not exist: e.g. a transport is provided - # with no credentials. Validation should still succeed because there is no - # mismatch with non-existent credentials. - channel = grpc.secure_channel( - "http://localhost/", grpc.local_channel_credentials() - ) - transport = transport_class(channel=channel) - transport._credentials = None - client = client_class(transport=transport) - assert client._validate_universe_domain() == True - - # TODO: This is needed to cater for older versions of google-auth - # Make this test unconditional once the minimum supported version of - # google-auth becomes 2.23.0 or higher. - google_auth_major, google_auth_minor = [ - int(part) for part in google.auth.__version__.split(".")[0:2] - ] - if google_auth_major > 2 or (google_auth_major == 2 and google_auth_minor >= 23): - credentials = ga_credentials.AnonymousCredentials() - credentials._universe_domain = "foo.com" - # Test the case when there is a universe mismatch from the credentials. - client = client_class(transport=transport_class(credentials=credentials)) - with pytest.raises(ValueError) as excinfo: - client._validate_universe_domain() - assert ( - str(excinfo.value) - == "The configured universe domain (googleapis.com) does not match the universe domain found in the credentials (foo.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default." - ) - - # Test the case when there is a universe mismatch from the client. - # - # TODO: Make this test unconditional once the minimum supported version of - # google-api-core becomes 2.15.0 or higher. - api_core_major, api_core_minor = [ - int(part) for part in api_core_version.__version__.split(".")[0:2] - ] - if api_core_major > 2 or (api_core_major == 2 and api_core_minor >= 15): - client = client_class( - client_options={"universe_domain": "bar.com"}, - transport=transport_class( - credentials=ga_credentials.AnonymousCredentials(), - ), - ) - with pytest.raises(ValueError) as excinfo: - client._validate_universe_domain() - assert ( - str(excinfo.value) - == "The configured universe domain (bar.com) does not match the universe domain found in the credentials (googleapis.com). If you haven't configured the universe domain explicitly, `googleapis.com` is the default." - ) - - # Test that ValueError is raised if universe_domain is provided via client options and credentials is None - with pytest.raises(ValueError): - client._compare_universes("foo.bar", None) - - @pytest.mark.parametrize( "client_class,transport_name", [ @@ -1145,27 +1086,6 @@ def test_create_subscription(request_type, transport: str = "grpc"): assert response.state == pubsub.Subscription.State.ACTIVE -def test_create_subscription_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.create_subscription), "__call__" - ) as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.create_subscription() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.Subscription() - - def test_create_subscription_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -1239,39 +1159,6 @@ def test_create_subscription_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_create_subscription_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.create_subscription), "__call__" - ) as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.Subscription( - name="name_value", - topic="topic_value", - ack_deadline_seconds=2066, - retain_acked_messages=True, - enable_message_ordering=True, - filter="filter_value", - detached=True, - enable_exactly_once_delivery=True, - state=pubsub.Subscription.State.ACTIVE, - ) - ) - response = await client.create_subscription() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.Subscription() - - @pytest.mark.asyncio async def test_create_subscription_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -1280,7 +1167,7 @@ async def test_create_subscription_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -1319,7 +1206,7 @@ async def test_create_subscription_async( transport: str = "grpc_asyncio", request_type=pubsub.Subscription ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -1405,7 +1292,7 @@ def test_create_subscription_field_headers(): @pytest.mark.asyncio async def test_create_subscription_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -1492,7 +1379,7 @@ def test_create_subscription_flattened_error(): @pytest.mark.asyncio async def test_create_subscription_flattened_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -1533,7 +1420,7 @@ async def test_create_subscription_flattened_async(): @pytest.mark.asyncio async def test_create_subscription_flattened_error_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -1600,25 +1487,6 @@ def test_get_subscription(request_type, transport: str = "grpc"): assert response.state == pubsub.Subscription.State.ACTIVE -def test_get_subscription_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.get_subscription), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.get_subscription() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.GetSubscriptionRequest() - - def test_get_subscription_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -1684,37 +1552,6 @@ def test_get_subscription_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_get_subscription_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.get_subscription), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.Subscription( - name="name_value", - topic="topic_value", - ack_deadline_seconds=2066, - retain_acked_messages=True, - enable_message_ordering=True, - filter="filter_value", - detached=True, - enable_exactly_once_delivery=True, - state=pubsub.Subscription.State.ACTIVE, - ) - ) - response = await client.get_subscription() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.GetSubscriptionRequest() - - @pytest.mark.asyncio async def test_get_subscription_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -1723,7 +1560,7 @@ async def test_get_subscription_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -1762,7 +1599,7 @@ async def test_get_subscription_async( transport: str = "grpc_asyncio", request_type=pubsub.GetSubscriptionRequest ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -1844,7 +1681,7 @@ def test_get_subscription_field_headers(): @pytest.mark.asyncio async def test_get_subscription_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -1912,7 +1749,7 @@ def test_get_subscription_flattened_error(): @pytest.mark.asyncio async def test_get_subscription_flattened_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -1939,7 +1776,7 @@ async def test_get_subscription_flattened_async(): @pytest.mark.asyncio async def test_get_subscription_flattened_error_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -2005,27 +1842,6 @@ def test_update_subscription(request_type, transport: str = "grpc"): assert response.state == pubsub.Subscription.State.ACTIVE -def test_update_subscription_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.update_subscription), "__call__" - ) as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.update_subscription() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.UpdateSubscriptionRequest() - - def test_update_subscription_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -2091,39 +1907,6 @@ def test_update_subscription_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_update_subscription_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.update_subscription), "__call__" - ) as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.Subscription( - name="name_value", - topic="topic_value", - ack_deadline_seconds=2066, - retain_acked_messages=True, - enable_message_ordering=True, - filter="filter_value", - detached=True, - enable_exactly_once_delivery=True, - state=pubsub.Subscription.State.ACTIVE, - ) - ) - response = await client.update_subscription() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.UpdateSubscriptionRequest() - - @pytest.mark.asyncio async def test_update_subscription_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -2132,7 +1915,7 @@ async def test_update_subscription_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -2171,7 +1954,7 @@ async def test_update_subscription_async( transport: str = "grpc_asyncio", request_type=pubsub.UpdateSubscriptionRequest ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -2257,7 +2040,7 @@ def test_update_subscription_field_headers(): @pytest.mark.asyncio async def test_update_subscription_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -2334,7 +2117,7 @@ def test_update_subscription_flattened_error(): @pytest.mark.asyncio async def test_update_subscription_flattened_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2367,7 +2150,7 @@ async def test_update_subscription_flattened_async(): @pytest.mark.asyncio async def test_update_subscription_flattened_error_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -2418,27 +2201,6 @@ def test_list_subscriptions(request_type, transport: str = "grpc"): assert response.next_page_token == "next_page_token_value" -def test_list_subscriptions_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.list_subscriptions), "__call__" - ) as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.list_subscriptions() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListSubscriptionsRequest() - - def test_list_subscriptions_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -2510,31 +2272,6 @@ def test_list_subscriptions_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_list_subscriptions_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.list_subscriptions), "__call__" - ) as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.ListSubscriptionsResponse( - next_page_token="next_page_token_value", - ) - ) - response = await client.list_subscriptions() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListSubscriptionsRequest() - - @pytest.mark.asyncio async def test_list_subscriptions_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -2543,7 +2280,7 @@ async def test_list_subscriptions_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -2582,7 +2319,7 @@ async def test_list_subscriptions_async( transport: str = "grpc_asyncio", request_type=pubsub.ListSubscriptionsRequest ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -2652,7 +2389,7 @@ def test_list_subscriptions_field_headers(): @pytest.mark.asyncio async def test_list_subscriptions_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -2726,7 +2463,7 @@ def test_list_subscriptions_flattened_error(): @pytest.mark.asyncio async def test_list_subscriptions_flattened_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2757,7 +2494,7 @@ async def test_list_subscriptions_flattened_async(): @pytest.mark.asyncio async def test_list_subscriptions_flattened_error_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -2871,7 +2608,7 @@ def test_list_subscriptions_pages(transport_name: str = "grpc"): @pytest.mark.asyncio async def test_list_subscriptions_async_pager(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -2923,7 +2660,7 @@ async def test_list_subscriptions_async_pager(): @pytest.mark.asyncio async def test_list_subscriptions_async_pages(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -3006,27 +2743,6 @@ def test_delete_subscription(request_type, transport: str = "grpc"): assert response is None -def test_delete_subscription_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.delete_subscription), "__call__" - ) as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.delete_subscription() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DeleteSubscriptionRequest() - - def test_delete_subscription_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -3097,37 +2813,16 @@ def test_delete_subscription_use_cached_wrapped_rpc(): @pytest.mark.asyncio -async def test_delete_subscription_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.delete_subscription), "__call__" - ) as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) - response = await client.delete_subscription() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DeleteSubscriptionRequest() - - -@pytest.mark.asyncio -async def test_delete_subscription_async_use_cached_wrapped_rpc( - transport: str = "grpc_asyncio", -): - # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, - # instead of constructing them on each call - with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) +async def test_delete_subscription_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport=transport, + ) # Should wrap all calls on client creation assert wrapper_fn.call_count > 0 @@ -3164,7 +2859,7 @@ async def test_delete_subscription_async( transport: str = "grpc_asyncio", request_type=pubsub.DeleteSubscriptionRequest ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -3229,7 +2924,7 @@ def test_delete_subscription_field_headers(): @pytest.mark.asyncio async def test_delete_subscription_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -3301,7 +2996,7 @@ def test_delete_subscription_flattened_error(): @pytest.mark.asyncio async def test_delete_subscription_flattened_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -3330,7 +3025,7 @@ async def test_delete_subscription_flattened_async(): @pytest.mark.asyncio async def test_delete_subscription_flattened_error_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -3377,27 +3072,6 @@ def test_modify_ack_deadline(request_type, transport: str = "grpc"): assert response is None -def test_modify_ack_deadline_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.modify_ack_deadline), "__call__" - ) as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.modify_ack_deadline() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ModifyAckDeadlineRequest() - - def test_modify_ack_deadline_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -3467,27 +3141,6 @@ def test_modify_ack_deadline_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_modify_ack_deadline_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.modify_ack_deadline), "__call__" - ) as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) - response = await client.modify_ack_deadline() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ModifyAckDeadlineRequest() - - @pytest.mark.asyncio async def test_modify_ack_deadline_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -3496,7 +3149,7 @@ async def test_modify_ack_deadline_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -3535,7 +3188,7 @@ async def test_modify_ack_deadline_async( transport: str = "grpc_asyncio", request_type=pubsub.ModifyAckDeadlineRequest ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -3600,7 +3253,7 @@ def test_modify_ack_deadline_field_headers(): @pytest.mark.asyncio async def test_modify_ack_deadline_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -3682,7 +3335,7 @@ def test_modify_ack_deadline_flattened_error(): @pytest.mark.asyncio async def test_modify_ack_deadline_flattened_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -3719,7 +3372,7 @@ async def test_modify_ack_deadline_flattened_async(): @pytest.mark.asyncio async def test_modify_ack_deadline_flattened_error_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -3766,25 +3419,6 @@ def test_acknowledge(request_type, transport: str = "grpc"): assert response is None -def test_acknowledge_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.acknowledge), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.acknowledge() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.AcknowledgeRequest() - - def test_acknowledge_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -3848,25 +3482,6 @@ def test_acknowledge_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_acknowledge_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.acknowledge), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) - response = await client.acknowledge() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.AcknowledgeRequest() - - @pytest.mark.asyncio async def test_acknowledge_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -3875,7 +3490,7 @@ async def test_acknowledge_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -3914,7 +3529,7 @@ async def test_acknowledge_async( transport: str = "grpc_asyncio", request_type=pubsub.AcknowledgeRequest ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -3975,7 +3590,7 @@ def test_acknowledge_field_headers(): @pytest.mark.asyncio async def test_acknowledge_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -4048,7 +3663,7 @@ def test_acknowledge_flattened_error(): @pytest.mark.asyncio async def test_acknowledge_flattened_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -4079,7 +3694,7 @@ async def test_acknowledge_flattened_async(): @pytest.mark.asyncio async def test_acknowledge_flattened_error_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -4125,25 +3740,6 @@ def test_pull(request_type, transport: str = "grpc"): assert isinstance(response, pubsub.PullResponse) -def test_pull_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.pull), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.pull() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.PullRequest() - - def test_pull_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -4207,32 +3803,13 @@ def test_pull_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_pull_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.pull), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(pubsub.PullResponse()) - response = await client.pull() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.PullRequest() - - @pytest.mark.asyncio async def test_pull_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio"): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4270,7 +3847,7 @@ async def test_pull_async( transport: str = "grpc_asyncio", request_type=pubsub.PullRequest ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4331,7 +3908,7 @@ def test_pull_field_headers(): @pytest.mark.asyncio async def test_pull_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -4411,7 +3988,7 @@ def test_pull_flattened_error(): @pytest.mark.asyncio async def test_pull_flattened_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -4448,7 +4025,7 @@ async def test_pull_flattened_async(): @pytest.mark.asyncio async def test_pull_flattened_error_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -4539,7 +4116,7 @@ async def test_streaming_pull_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4578,7 +4155,7 @@ async def test_streaming_pull_async( transport: str = "grpc_asyncio", request_type=pubsub.StreamingPullRequest ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4646,27 +4223,6 @@ def test_modify_push_config(request_type, transport: str = "grpc"): assert response is None -def test_modify_push_config_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.modify_push_config), "__call__" - ) as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.modify_push_config() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ModifyPushConfigRequest() - - def test_modify_push_config_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -4736,27 +4292,6 @@ def test_modify_push_config_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_modify_push_config_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client.transport.modify_push_config), "__call__" - ) as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) - response = await client.modify_push_config() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ModifyPushConfigRequest() - - @pytest.mark.asyncio async def test_modify_push_config_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -4765,7 +4300,7 @@ async def test_modify_push_config_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4804,7 +4339,7 @@ async def test_modify_push_config_async( transport: str = "grpc_asyncio", request_type=pubsub.ModifyPushConfigRequest ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -4869,7 +4404,7 @@ def test_modify_push_config_field_headers(): @pytest.mark.asyncio async def test_modify_push_config_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -4946,7 +4481,7 @@ def test_modify_push_config_flattened_error(): @pytest.mark.asyncio async def test_modify_push_config_flattened_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -4979,7 +4514,7 @@ async def test_modify_push_config_flattened_async(): @pytest.mark.asyncio async def test_modify_push_config_flattened_error_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -5030,25 +4565,6 @@ def test_get_snapshot(request_type, transport: str = "grpc"): assert response.topic == "topic_value" -def test_get_snapshot_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.get_snapshot), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.get_snapshot() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.GetSnapshotRequest() - - def test_get_snapshot_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -5112,30 +4628,6 @@ def test_get_snapshot_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_get_snapshot_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.get_snapshot), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.Snapshot( - name="name_value", - topic="topic_value", - ) - ) - response = await client.get_snapshot() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.GetSnapshotRequest() - - @pytest.mark.asyncio async def test_get_snapshot_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -5144,7 +4636,7 @@ async def test_get_snapshot_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -5183,7 +4675,7 @@ async def test_get_snapshot_async( transport: str = "grpc_asyncio", request_type=pubsub.GetSnapshotRequest ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -5251,7 +4743,7 @@ def test_get_snapshot_field_headers(): @pytest.mark.asyncio async def test_get_snapshot_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -5319,7 +4811,7 @@ def test_get_snapshot_flattened_error(): @pytest.mark.asyncio async def test_get_snapshot_flattened_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -5346,7 +4838,7 @@ async def test_get_snapshot_flattened_async(): @pytest.mark.asyncio async def test_get_snapshot_flattened_error_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -5394,25 +4886,6 @@ def test_list_snapshots(request_type, transport: str = "grpc"): assert response.next_page_token == "next_page_token_value" -def test_list_snapshots_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.list_snapshots), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.list_snapshots() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListSnapshotsRequest() - - def test_list_snapshots_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -5478,29 +4951,6 @@ def test_list_snapshots_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_list_snapshots_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.list_snapshots), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.ListSnapshotsResponse( - next_page_token="next_page_token_value", - ) - ) - response = await client.list_snapshots() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.ListSnapshotsRequest() - - @pytest.mark.asyncio async def test_list_snapshots_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -5509,7 +4959,7 @@ async def test_list_snapshots_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -5548,7 +4998,7 @@ async def test_list_snapshots_async( transport: str = "grpc_asyncio", request_type=pubsub.ListSnapshotsRequest ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -5614,7 +5064,7 @@ def test_list_snapshots_field_headers(): @pytest.mark.asyncio async def test_list_snapshots_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -5684,7 +5134,7 @@ def test_list_snapshots_flattened_error(): @pytest.mark.asyncio async def test_list_snapshots_flattened_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -5713,7 +5163,7 @@ async def test_list_snapshots_flattened_async(): @pytest.mark.asyncio async def test_list_snapshots_flattened_error_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -5823,7 +5273,7 @@ def test_list_snapshots_pages(transport_name: str = "grpc"): @pytest.mark.asyncio async def test_list_snapshots_async_pager(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -5873,7 +5323,7 @@ async def test_list_snapshots_async_pager(): @pytest.mark.asyncio async def test_list_snapshots_async_pages(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -5957,39 +5407,20 @@ def test_create_snapshot(request_type, transport: str = "grpc"): assert response.topic == "topic_value" -def test_create_snapshot_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. +def test_create_snapshot_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.create_snapshot), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.create_snapshot() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.CreateSnapshotRequest() - - -def test_create_snapshot_non_empty_request_with_auto_populated_field(): - # This test is a coverage failsafe to make sure that UUID4 fields are - # automatically populated, according to AIP-4235, with non-empty requests. - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Populate all string fields in the request which are not UUID4 - # since we want to check that UUID4 are populated automatically - # if they meet the requirements of AIP 4235. - request = pubsub.CreateSnapshotRequest( - name="name_value", - subscription="subscription_value", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = pubsub.CreateSnapshotRequest( + name="name_value", + subscription="subscription_value", ) # Mock the actual call within the gRPC stub, and fake the request. @@ -6041,30 +5472,6 @@ def test_create_snapshot_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_create_snapshot_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.create_snapshot), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.Snapshot( - name="name_value", - topic="topic_value", - ) - ) - response = await client.create_snapshot() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.CreateSnapshotRequest() - - @pytest.mark.asyncio async def test_create_snapshot_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -6073,7 +5480,7 @@ async def test_create_snapshot_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -6112,7 +5519,7 @@ async def test_create_snapshot_async( transport: str = "grpc_asyncio", request_type=pubsub.CreateSnapshotRequest ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -6180,7 +5587,7 @@ def test_create_snapshot_field_headers(): @pytest.mark.asyncio async def test_create_snapshot_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -6253,7 +5660,7 @@ def test_create_snapshot_flattened_error(): @pytest.mark.asyncio async def test_create_snapshot_flattened_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -6284,7 +5691,7 @@ async def test_create_snapshot_flattened_async(): @pytest.mark.asyncio async def test_create_snapshot_flattened_error_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -6335,25 +5742,6 @@ def test_update_snapshot(request_type, transport: str = "grpc"): assert response.topic == "topic_value" -def test_update_snapshot_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.update_snapshot), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.update_snapshot() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.UpdateSnapshotRequest() - - def test_update_snapshot_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -6413,30 +5801,6 @@ def test_update_snapshot_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_update_snapshot_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.update_snapshot), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( - pubsub.Snapshot( - name="name_value", - topic="topic_value", - ) - ) - response = await client.update_snapshot() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.UpdateSnapshotRequest() - - @pytest.mark.asyncio async def test_update_snapshot_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -6445,7 +5809,7 @@ async def test_update_snapshot_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -6484,7 +5848,7 @@ async def test_update_snapshot_async( transport: str = "grpc_asyncio", request_type=pubsub.UpdateSnapshotRequest ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -6552,7 +5916,7 @@ def test_update_snapshot_field_headers(): @pytest.mark.asyncio async def test_update_snapshot_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -6625,7 +5989,7 @@ def test_update_snapshot_flattened_error(): @pytest.mark.asyncio async def test_update_snapshot_flattened_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -6656,7 +6020,7 @@ async def test_update_snapshot_flattened_async(): @pytest.mark.asyncio async def test_update_snapshot_flattened_error_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -6702,25 +6066,6 @@ def test_delete_snapshot(request_type, transport: str = "grpc"): assert response is None -def test_delete_snapshot_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.delete_snapshot), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.delete_snapshot() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DeleteSnapshotRequest() - - def test_delete_snapshot_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -6784,25 +6129,6 @@ def test_delete_snapshot_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_delete_snapshot_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.delete_snapshot), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) - response = await client.delete_snapshot() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.DeleteSnapshotRequest() - - @pytest.mark.asyncio async def test_delete_snapshot_async_use_cached_wrapped_rpc( transport: str = "grpc_asyncio", @@ -6811,7 +6137,7 @@ async def test_delete_snapshot_async_use_cached_wrapped_rpc( # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -6850,7 +6176,7 @@ async def test_delete_snapshot_async( transport: str = "grpc_asyncio", request_type=pubsub.DeleteSnapshotRequest ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -6911,7 +6237,7 @@ def test_delete_snapshot_field_headers(): @pytest.mark.asyncio async def test_delete_snapshot_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -6979,7 +6305,7 @@ def test_delete_snapshot_flattened_error(): @pytest.mark.asyncio async def test_delete_snapshot_flattened_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. @@ -7006,7 +6332,7 @@ async def test_delete_snapshot_flattened_async(): @pytest.mark.asyncio async def test_delete_snapshot_flattened_error_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Attempting to call a method with both a request object and flattened @@ -7051,25 +6377,6 @@ def test_seek(request_type, transport: str = "grpc"): assert isinstance(response, pubsub.SeekResponse) -def test_seek_empty_call(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.seek), "__call__") as call: - call.return_value.name = ( - "foo" # operation_request.operation in compute client(s) expect a string. - ) - client.seek() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.SeekRequest() - - def test_seek_non_empty_request_with_auto_populated_field(): # This test is a coverage failsafe to make sure that UUID4 fields are # automatically populated, according to AIP-4235, with non-empty requests. @@ -7135,32 +6442,13 @@ def test_seek_use_cached_wrapped_rpc(): assert mock_rpc.call_count == 2 -@pytest.mark.asyncio -async def test_seek_empty_call_async(): - # This test is a coverage failsafe to make sure that totally empty calls, - # i.e. request == None and no flattened fields passed, work. - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - - # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client.transport.seek), "__call__") as call: - # Designate an appropriate return value for the call. - call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(pubsub.SeekResponse()) - response = await client.seek() - call.assert_called() - _, args, _ = call.mock_calls[0] - assert args[0] == pubsub.SeekRequest() - - @pytest.mark.asyncio async def test_seek_async_use_cached_wrapped_rpc(transport: str = "grpc_asyncio"): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -7198,7 +6486,7 @@ async def test_seek_async( transport: str = "grpc_asyncio", request_type=pubsub.SeekRequest ): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -7259,7 +6547,7 @@ def test_seek_field_headers(): @pytest.mark.asyncio async def test_seek_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -7286,62 +6574,6 @@ async def test_seek_field_headers_async(): ) in kw["metadata"] -@pytest.mark.parametrize( - "request_type", - [ - pubsub.Subscription, - dict, - ], -) -def test_create_subscription_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.Subscription( - name="name_value", - topic="topic_value", - ack_deadline_seconds=2066, - retain_acked_messages=True, - enable_message_ordering=True, - filter="filter_value", - detached=True, - enable_exactly_once_delivery=True, - state=pubsub.Subscription.State.ACTIVE, - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.Subscription.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.create_subscription(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pubsub.Subscription) - assert response.name == "name_value" - assert response.topic == "topic_value" - assert response.ack_deadline_seconds == 2066 - assert response.retain_acked_messages is True - assert response.enable_message_ordering is True - assert response.filter == "filter_value" - assert response.detached is True - assert response.enable_exactly_once_delivery is True - assert response.state == pubsub.Subscription.State.ACTIVE - - def test_create_subscription_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -7476,96 +6708,19 @@ def test_create_subscription_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_create_subscription_rest_interceptors(null_interceptor): - transport = transports.SubscriberRestTransport( +def test_create_subscription_rest_flattened(): + client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SubscriberRestInterceptor(), + transport="rest", ) - client = SubscriberClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SubscriberRestInterceptor, "post_create_subscription" - ) as post, mock.patch.object( - transports.SubscriberRestInterceptor, "pre_create_subscription" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.Subscription.pb(pubsub.Subscription()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.Subscription.to_json(pubsub.Subscription()) + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.Subscription() - request = pubsub.Subscription() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.Subscription() - - client.create_subscription( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_create_subscription_rest_bad_request( - transport: str = "rest", request_type=pubsub.Subscription -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.create_subscription(request) - - -def test_create_subscription_rest_flattened(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.Subscription() - - # get arguments that satisfy an http rule for this method - sample_request = {"name": "projects/sample1/subscriptions/sample2"} + # get arguments that satisfy an http rule for this method + sample_request = {"name": "projects/sample1/subscriptions/sample2"} # get truthy value for each flattened field mock_args = dict( @@ -7614,68 +6769,6 @@ def test_create_subscription_rest_flattened_error(transport: str = "rest"): ) -def test_create_subscription_rest_error(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.GetSubscriptionRequest, - dict, - ], -) -def test_get_subscription_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.Subscription( - name="name_value", - topic="topic_value", - ack_deadline_seconds=2066, - retain_acked_messages=True, - enable_message_ordering=True, - filter="filter_value", - detached=True, - enable_exactly_once_delivery=True, - state=pubsub.Subscription.State.ACTIVE, - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.Subscription.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.get_subscription(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pubsub.Subscription) - assert response.name == "name_value" - assert response.topic == "topic_value" - assert response.ack_deadline_seconds == 2066 - assert response.retain_acked_messages is True - assert response.enable_message_ordering is True - assert response.filter == "filter_value" - assert response.detached is True - assert response.enable_exactly_once_delivery is True - assert response.state == pubsub.Subscription.State.ACTIVE - - def test_get_subscription_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -7797,83 +6890,6 @@ def test_get_subscription_rest_unset_required_fields(): assert set(unset_fields) == (set(()) & set(("subscription",))) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_get_subscription_rest_interceptors(null_interceptor): - transport = transports.SubscriberRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SubscriberRestInterceptor(), - ) - client = SubscriberClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SubscriberRestInterceptor, "post_get_subscription" - ) as post, mock.patch.object( - transports.SubscriberRestInterceptor, "pre_get_subscription" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.GetSubscriptionRequest.pb(pubsub.GetSubscriptionRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.Subscription.to_json(pubsub.Subscription()) - - request = pubsub.GetSubscriptionRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.Subscription() - - client.get_subscription( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_get_subscription_rest_bad_request( - transport: str = "rest", request_type=pubsub.GetSubscriptionRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.get_subscription(request) - - def test_get_subscription_rest_flattened(): client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), @@ -7930,68 +6946,6 @@ def test_get_subscription_rest_flattened_error(transport: str = "rest"): ) -def test_get_subscription_rest_error(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.UpdateSubscriptionRequest, - dict, - ], -) -def test_update_subscription_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": {"name": "projects/sample1/subscriptions/sample2"}} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.Subscription( - name="name_value", - topic="topic_value", - ack_deadline_seconds=2066, - retain_acked_messages=True, - enable_message_ordering=True, - filter="filter_value", - detached=True, - enable_exactly_once_delivery=True, - state=pubsub.Subscription.State.ACTIVE, - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.Subscription.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.update_subscription(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pubsub.Subscription) - assert response.name == "name_value" - assert response.topic == "topic_value" - assert response.ack_deadline_seconds == 2066 - assert response.retain_acked_messages is True - assert response.enable_message_ordering is True - assert response.filter == "filter_value" - assert response.detached is True - assert response.enable_exactly_once_delivery is True - assert response.state == pubsub.Subscription.State.ACTIVE - - def test_update_subscription_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -8119,89 +7073,10 @@ def test_update_subscription_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_update_subscription_rest_interceptors(null_interceptor): - transport = transports.SubscriberRestTransport( +def test_update_subscription_rest_flattened(): + client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SubscriberRestInterceptor(), - ) - client = SubscriberClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SubscriberRestInterceptor, "post_update_subscription" - ) as post, mock.patch.object( - transports.SubscriberRestInterceptor, "pre_update_subscription" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.UpdateSubscriptionRequest.pb( - pubsub.UpdateSubscriptionRequest() - ) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.Subscription.to_json(pubsub.Subscription()) - - request = pubsub.UpdateSubscriptionRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.Subscription() - - client.update_subscription( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_update_subscription_rest_bad_request( - transport: str = "rest", request_type=pubsub.UpdateSubscriptionRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": {"name": "projects/sample1/subscriptions/sample2"}} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.update_subscription(request) - - -def test_update_subscription_rest_flattened(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", + transport="rest", ) # Mock the http request call within the method and fake a response. @@ -8259,52 +7134,6 @@ def test_update_subscription_rest_flattened_error(transport: str = "rest"): ) -def test_update_subscription_rest_error(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.ListSubscriptionsRequest, - dict, - ], -) -def test_list_subscriptions_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"project": "projects/sample1"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.ListSubscriptionsResponse( - next_page_token="next_page_token_value", - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.ListSubscriptionsResponse.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.list_subscriptions(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pagers.ListSubscriptionsPager) - assert response.next_page_token == "next_page_token_value" - - def test_list_subscriptions_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -8443,87 +7272,6 @@ def test_list_subscriptions_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_list_subscriptions_rest_interceptors(null_interceptor): - transport = transports.SubscriberRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SubscriberRestInterceptor(), - ) - client = SubscriberClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SubscriberRestInterceptor, "post_list_subscriptions" - ) as post, mock.patch.object( - transports.SubscriberRestInterceptor, "pre_list_subscriptions" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.ListSubscriptionsRequest.pb( - pubsub.ListSubscriptionsRequest() - ) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.ListSubscriptionsResponse.to_json( - pubsub.ListSubscriptionsResponse() - ) - - request = pubsub.ListSubscriptionsRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.ListSubscriptionsResponse() - - client.list_subscriptions( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_list_subscriptions_rest_bad_request( - transport: str = "rest", request_type=pubsub.ListSubscriptionsRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"project": "projects/sample1"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.list_subscriptions(request) - - def test_list_subscriptions_rest_flattened(): client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), @@ -8640,41 +7388,6 @@ def test_list_subscriptions_rest_pager(transport: str = "rest"): assert page_.raw_page.next_page_token == token -@pytest.mark.parametrize( - "request_type", - [ - pubsub.DeleteSubscriptionRequest, - dict, - ], -) -def test_delete_subscription_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = None - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = "" - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.delete_subscription(request) - - # Establish that the response is the type that we expect. - assert response is None - - def test_delete_subscription_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -8795,92 +7508,19 @@ def test_delete_subscription_rest_unset_required_fields(): assert set(unset_fields) == (set(()) & set(("subscription",))) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_delete_subscription_rest_interceptors(null_interceptor): - transport = transports.SubscriberRestTransport( +def test_delete_subscription_rest_flattened(): + client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SubscriberRestInterceptor(), + transport="rest", ) - client = SubscriberClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SubscriberRestInterceptor, "pre_delete_subscription" - ) as pre: - pre.assert_not_called() - pb_message = pubsub.DeleteSubscriptionRequest.pb( - pubsub.DeleteSubscriptionRequest() - ) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = None - request = pubsub.DeleteSubscriptionRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - - client.delete_subscription( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - - -def test_delete_subscription_rest_bad_request( - transport: str = "rest", request_type=pubsub.DeleteSubscriptionRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.delete_subscription(request) - - -def test_delete_subscription_rest_flattened(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = None - - # get arguments that satisfy an http rule for this method - sample_request = {"subscription": "projects/sample1/subscriptions/sample2"} + # get arguments that satisfy an http rule for this method + sample_request = {"subscription": "projects/sample1/subscriptions/sample2"} # get truthy value for each flattened field mock_args = dict( @@ -8922,47 +7562,6 @@ def test_delete_subscription_rest_flattened_error(transport: str = "rest"): ) -def test_delete_subscription_rest_error(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.ModifyAckDeadlineRequest, - dict, - ], -) -def test_modify_ack_deadline_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = None - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = "" - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.modify_ack_deadline(request) - - # Establish that the response is the type that we expect. - assert response is None - - def test_modify_ack_deadline_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -9101,79 +7700,6 @@ def test_modify_ack_deadline_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_modify_ack_deadline_rest_interceptors(null_interceptor): - transport = transports.SubscriberRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SubscriberRestInterceptor(), - ) - client = SubscriberClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SubscriberRestInterceptor, "pre_modify_ack_deadline" - ) as pre: - pre.assert_not_called() - pb_message = pubsub.ModifyAckDeadlineRequest.pb( - pubsub.ModifyAckDeadlineRequest() - ) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - - request = pubsub.ModifyAckDeadlineRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - - client.modify_ack_deadline( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - - -def test_modify_ack_deadline_rest_bad_request( - transport: str = "rest", request_type=pubsub.ModifyAckDeadlineRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.modify_ack_deadline(request) - - def test_modify_ack_deadline_rest_flattened(): client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), @@ -9233,47 +7759,6 @@ def test_modify_ack_deadline_rest_flattened_error(transport: str = "rest"): ) -def test_modify_ack_deadline_rest_error(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.AcknowledgeRequest, - dict, - ], -) -def test_acknowledge_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = None - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = "" - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.acknowledge(request) - - # Establish that the response is the type that we expect. - assert response is None - - def test_acknowledge_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -9401,106 +7886,35 @@ def test_acknowledge_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_acknowledge_rest_interceptors(null_interceptor): - transport = transports.SubscriberRestTransport( +def test_acknowledge_rest_flattened(): + client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SubscriberRestInterceptor(), + transport="rest", ) - client = SubscriberClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SubscriberRestInterceptor, "pre_acknowledge" - ) as pre: - pre.assert_not_called() - pb_message = pubsub.AcknowledgeRequest.pb(pubsub.AcknowledgeRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = None - request = pubsub.AcknowledgeRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata + # get arguments that satisfy an http rule for this method + sample_request = {"subscription": "projects/sample1/subscriptions/sample2"} - client.acknowledge( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], + # get truthy value for each flattened field + mock_args = dict( + subscription="subscription_value", + ack_ids=["ack_ids_value"], ) + mock_args.update(sample_request) - pre.assert_called_once() + # Wrap the value into a proper Response obj + response_value = Response() + response_value.status_code = 200 + json_return_value = "" + response_value._content = json_return_value.encode("UTF-8") + req.return_value = response_value - -def test_acknowledge_rest_bad_request( - transport: str = "rest", request_type=pubsub.AcknowledgeRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.acknowledge(request) - - -def test_acknowledge_rest_flattened(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = None - - # get arguments that satisfy an http rule for this method - sample_request = {"subscription": "projects/sample1/subscriptions/sample2"} - - # get truthy value for each flattened field - mock_args = dict( - subscription="subscription_value", - ack_ids=["ack_ids_value"], - ) - mock_args.update(sample_request) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = "" - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - - client.acknowledge(**mock_args) + client.acknowledge(**mock_args) # Establish that the underlying call was made with the expected # request object values. @@ -9529,49 +7943,6 @@ def test_acknowledge_rest_flattened_error(transport: str = "rest"): ) -def test_acknowledge_rest_error(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.PullRequest, - dict, - ], -) -def test_pull_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.PullResponse() - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.PullResponse.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.pull(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pubsub.PullResponse) - - def test_pull_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -9702,83 +8073,6 @@ def test_pull_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_pull_rest_interceptors(null_interceptor): - transport = transports.SubscriberRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SubscriberRestInterceptor(), - ) - client = SubscriberClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SubscriberRestInterceptor, "post_pull" - ) as post, mock.patch.object( - transports.SubscriberRestInterceptor, "pre_pull" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.PullRequest.pb(pubsub.PullRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.PullResponse.to_json(pubsub.PullResponse()) - - request = pubsub.PullRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.PullResponse() - - client.pull( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_pull_rest_bad_request( - transport: str = "rest", request_type=pubsub.PullRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.pull(request) - - def test_pull_rest_flattened(): client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), @@ -9840,12 +8134,6 @@ def test_pull_rest_flattened_error(transport: str = "rest"): ) -def test_pull_rest_error(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - def test_streaming_pull_rest_no_http_options(): client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), @@ -9857,41 +8145,6 @@ def test_streaming_pull_rest_no_http_options(): client.streaming_pull(requests) -@pytest.mark.parametrize( - "request_type", - [ - pubsub.ModifyPushConfigRequest, - dict, - ], -) -def test_modify_push_config_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = None - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = "" - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.modify_push_config(request) - - # Establish that the response is the type that we expect. - assert response is None - - def test_modify_push_config_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -10021,106 +8274,35 @@ def test_modify_push_config_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_modify_push_config_rest_interceptors(null_interceptor): - transport = transports.SubscriberRestTransport( +def test_modify_push_config_rest_flattened(): + client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SubscriberRestInterceptor(), + transport="rest", ) - client = SubscriberClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SubscriberRestInterceptor, "pre_modify_push_config" - ) as pre: - pre.assert_not_called() - pb_message = pubsub.ModifyPushConfigRequest.pb(pubsub.ModifyPushConfigRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = None - request = pubsub.ModifyPushConfigRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata + # get arguments that satisfy an http rule for this method + sample_request = {"subscription": "projects/sample1/subscriptions/sample2"} - client.modify_push_config( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], + # get truthy value for each flattened field + mock_args = dict( + subscription="subscription_value", + push_config=pubsub.PushConfig(push_endpoint="push_endpoint_value"), ) + mock_args.update(sample_request) - pre.assert_called_once() + # Wrap the value into a proper Response obj + response_value = Response() + response_value.status_code = 200 + json_return_value = "" + response_value._content = json_return_value.encode("UTF-8") + req.return_value = response_value - -def test_modify_push_config_rest_bad_request( - transport: str = "rest", request_type=pubsub.ModifyPushConfigRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.modify_push_config(request) - - -def test_modify_push_config_rest_flattened(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = None - - # get arguments that satisfy an http rule for this method - sample_request = {"subscription": "projects/sample1/subscriptions/sample2"} - - # get truthy value for each flattened field - mock_args = dict( - subscription="subscription_value", - push_config=pubsub.PushConfig(push_endpoint="push_endpoint_value"), - ) - mock_args.update(sample_request) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = "" - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - - client.modify_push_config(**mock_args) + client.modify_push_config(**mock_args) # Establish that the underlying call was made with the expected # request object values. @@ -10149,54 +8331,6 @@ def test_modify_push_config_rest_flattened_error(transport: str = "rest"): ) -def test_modify_push_config_rest_error(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.GetSnapshotRequest, - dict, - ], -) -def test_get_snapshot_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"snapshot": "projects/sample1/snapshots/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.Snapshot( - name="name_value", - topic="topic_value", - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.Snapshot.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.get_snapshot(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pubsub.Snapshot) - assert response.name == "name_value" - assert response.topic == "topic_value" - - def test_get_snapshot_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -10314,83 +8448,6 @@ def test_get_snapshot_rest_unset_required_fields(): assert set(unset_fields) == (set(()) & set(("snapshot",))) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_get_snapshot_rest_interceptors(null_interceptor): - transport = transports.SubscriberRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SubscriberRestInterceptor(), - ) - client = SubscriberClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SubscriberRestInterceptor, "post_get_snapshot" - ) as post, mock.patch.object( - transports.SubscriberRestInterceptor, "pre_get_snapshot" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.GetSnapshotRequest.pb(pubsub.GetSnapshotRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.Snapshot.to_json(pubsub.Snapshot()) - - request = pubsub.GetSnapshotRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.Snapshot() - - client.get_snapshot( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_get_snapshot_rest_bad_request( - transport: str = "rest", request_type=pubsub.GetSnapshotRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"snapshot": "projects/sample1/snapshots/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.get_snapshot(request) - - def test_get_snapshot_rest_flattened(): client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), @@ -10446,52 +8503,6 @@ def test_get_snapshot_rest_flattened_error(transport: str = "rest"): ) -def test_get_snapshot_rest_error(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.ListSnapshotsRequest, - dict, - ], -) -def test_list_snapshots_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"project": "projects/sample1"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.ListSnapshotsResponse( - next_page_token="next_page_token_value", - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.ListSnapshotsResponse.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.list_snapshots(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pagers.ListSnapshotsPager) - assert response.next_page_token == "next_page_token_value" - - def test_list_snapshots_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -10624,95 +8635,16 @@ def test_list_snapshots_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_list_snapshots_rest_interceptors(null_interceptor): - transport = transports.SubscriberRestTransport( +def test_list_snapshots_rest_flattened(): + client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SubscriberRestInterceptor(), + transport="rest", ) - client = SubscriberClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SubscriberRestInterceptor, "post_list_snapshots" - ) as post, mock.patch.object( - transports.SubscriberRestInterceptor, "pre_list_snapshots" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.ListSnapshotsRequest.pb(pubsub.ListSnapshotsRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.ListSnapshotsResponse.to_json( - pubsub.ListSnapshotsResponse() - ) - - request = pubsub.ListSnapshotsRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.ListSnapshotsResponse() - - client.list_snapshots( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_list_snapshots_rest_bad_request( - transport: str = "rest", request_type=pubsub.ListSnapshotsRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"project": "projects/sample1"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.list_snapshots(request) - - -def test_list_snapshots_rest_flattened(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.ListSnapshotsResponse() + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.ListSnapshotsResponse() # get arguments that satisfy an http rule for this method sample_request = {"project": "projects/sample1"} @@ -10819,48 +8751,6 @@ def test_list_snapshots_rest_pager(transport: str = "rest"): assert page_.raw_page.next_page_token == token -@pytest.mark.parametrize( - "request_type", - [ - pubsub.CreateSnapshotRequest, - dict, - ], -) -def test_create_snapshot_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/snapshots/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.Snapshot( - name="name_value", - topic="topic_value", - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.Snapshot.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.create_snapshot(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pubsub.Snapshot) - assert response.name == "name_value" - assert response.topic == "topic_value" - - def test_create_snapshot_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -10993,83 +8883,6 @@ def test_create_snapshot_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_create_snapshot_rest_interceptors(null_interceptor): - transport = transports.SubscriberRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SubscriberRestInterceptor(), - ) - client = SubscriberClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SubscriberRestInterceptor, "post_create_snapshot" - ) as post, mock.patch.object( - transports.SubscriberRestInterceptor, "pre_create_snapshot" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.CreateSnapshotRequest.pb(pubsub.CreateSnapshotRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.Snapshot.to_json(pubsub.Snapshot()) - - request = pubsub.CreateSnapshotRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.Snapshot() - - client.create_snapshot( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_create_snapshot_rest_bad_request( - transport: str = "rest", request_type=pubsub.CreateSnapshotRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"name": "projects/sample1/snapshots/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.create_snapshot(request) - - def test_create_snapshot_rest_flattened(): client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), @@ -11127,54 +8940,6 @@ def test_create_snapshot_rest_flattened_error(transport: str = "rest"): ) -def test_create_snapshot_rest_error(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.UpdateSnapshotRequest, - dict, - ], -) -def test_update_snapshot_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"snapshot": {"name": "projects/sample1/snapshots/sample2"}} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.Snapshot( - name="name_value", - topic="topic_value", - ) - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.Snapshot.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.update_snapshot(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pubsub.Snapshot) - assert response.name == "name_value" - assert response.topic == "topic_value" - - def test_update_snapshot_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -11298,96 +9063,19 @@ def test_update_snapshot_rest_unset_required_fields(): ) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_update_snapshot_rest_interceptors(null_interceptor): - transport = transports.SubscriberRestTransport( +def test_update_snapshot_rest_flattened(): + client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SubscriberRestInterceptor(), + transport="rest", ) - client = SubscriberClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SubscriberRestInterceptor, "post_update_snapshot" - ) as post, mock.patch.object( - transports.SubscriberRestInterceptor, "pre_update_snapshot" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.UpdateSnapshotRequest.pb(pubsub.UpdateSnapshotRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.Snapshot.to_json(pubsub.Snapshot()) - request = pubsub.UpdateSnapshotRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.Snapshot() + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.Snapshot() - client.update_snapshot( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - post.assert_called_once() - - -def test_update_snapshot_rest_bad_request( - transport: str = "rest", request_type=pubsub.UpdateSnapshotRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"snapshot": {"name": "projects/sample1/snapshots/sample2"}} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.update_snapshot(request) - - -def test_update_snapshot_rest_flattened(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.Snapshot() - - # get arguments that satisfy an http rule for this method - sample_request = {"snapshot": {"name": "projects/sample1/snapshots/sample2"}} + # get arguments that satisfy an http rule for this method + sample_request = {"snapshot": {"name": "projects/sample1/snapshots/sample2"}} # get truthy value for each flattened field mock_args = dict( @@ -11433,47 +9121,6 @@ def test_update_snapshot_rest_flattened_error(transport: str = "rest"): ) -def test_update_snapshot_rest_error(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.DeleteSnapshotRequest, - dict, - ], -) -def test_delete_snapshot_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"snapshot": "projects/sample1/snapshots/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = None - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = "" - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.delete_snapshot(request) - - # Establish that the response is the type that we expect. - assert response is None - - def test_delete_snapshot_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -11590,77 +9237,6 @@ def test_delete_snapshot_rest_unset_required_fields(): assert set(unset_fields) == (set(()) & set(("snapshot",))) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_delete_snapshot_rest_interceptors(null_interceptor): - transport = transports.SubscriberRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SubscriberRestInterceptor(), - ) - client = SubscriberClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SubscriberRestInterceptor, "pre_delete_snapshot" - ) as pre: - pre.assert_not_called() - pb_message = pubsub.DeleteSnapshotRequest.pb(pubsub.DeleteSnapshotRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - - request = pubsub.DeleteSnapshotRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - - client.delete_snapshot( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], - ) - - pre.assert_called_once() - - -def test_delete_snapshot_rest_bad_request( - transport: str = "rest", request_type=pubsub.DeleteSnapshotRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"snapshot": "projects/sample1/snapshots/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.delete_snapshot(request) - - def test_delete_snapshot_rest_flattened(): client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), @@ -11714,49 +9290,6 @@ def test_delete_snapshot_rest_flattened_error(transport: str = "rest"): ) -def test_delete_snapshot_rest_error(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -@pytest.mark.parametrize( - "request_type", - [ - pubsub.SeekRequest, - dict, - ], -) -def test_seek_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = pubsub.SeekResponse() - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - # Convert return value to protobuf type - return_value = pubsub.SeekResponse.pb(return_value) - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - response = client.seek(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, pubsub.SeekResponse) - - def test_seek_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -11875,115 +9408,32 @@ def test_seek_rest_unset_required_fields(): assert set(unset_fields) == (set(()) & set(("subscription",))) -@pytest.mark.parametrize("null_interceptor", [True, False]) -def test_seek_rest_interceptors(null_interceptor): - transport = transports.SubscriberRestTransport( - credentials=ga_credentials.AnonymousCredentials(), - interceptor=None - if null_interceptor - else transports.SubscriberRestInterceptor(), +def test_streaming_pull_rest_error(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # Since a `google.api.http` annotation is required for using a rest transport + # method, this should error. + with pytest.raises(NotImplementedError) as not_implemented_error: + client.streaming_pull({}) + assert "Method StreamingPull is not available over REST transport" in str( + not_implemented_error.value ) - client = SubscriberClient(transport=transport) - with mock.patch.object( - type(client.transport._session), "request" - ) as req, mock.patch.object( - path_template, "transcode" - ) as transcode, mock.patch.object( - transports.SubscriberRestInterceptor, "post_seek" - ) as post, mock.patch.object( - transports.SubscriberRestInterceptor, "pre_seek" - ) as pre: - pre.assert_not_called() - post.assert_not_called() - pb_message = pubsub.SeekRequest.pb(pubsub.SeekRequest()) - transcode.return_value = { - "method": "post", - "uri": "my_uri", - "body": pb_message, - "query_params": pb_message, - } - - req.return_value = Response() - req.return_value.status_code = 200 - req.return_value.request = PreparedRequest() - req.return_value._content = pubsub.SeekResponse.to_json(pubsub.SeekResponse()) - request = pubsub.SeekRequest() - metadata = [ - ("key", "val"), - ("cephalopod", "squid"), - ] - pre.return_value = request, metadata - post.return_value = pubsub.SeekResponse() - client.seek( - request, - metadata=[ - ("key", "val"), - ("cephalopod", "squid"), - ], +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.SubscriberGrpcTransport( + credentials=ga_credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, ) - pre.assert_called_once() - post.assert_called_once() - - -def test_seek_rest_bad_request( - transport: str = "rest", request_type=pubsub.SeekRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # send a request that will satisfy transcoding - request_init = {"subscription": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.seek(request) - - -def test_seek_rest_error(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - - -def test_streaming_pull_rest_error(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), transport="rest" - ) - # Since a `google.api.http` annotation is required for using a rest transport - # method, this should error. - with pytest.raises(NotImplementedError) as not_implemented_error: - client.streaming_pull({}) - assert "Method StreamingPull is not available over REST transport" in str( - not_implemented_error.value - ) - - -def test_credentials_transport_error(): - # It is an error to provide credentials and a transport instance. - transport = transports.SubscriberGrpcTransport( - credentials=ga_credentials.AnonymousCredentials(), - ) - with pytest.raises(ValueError): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - # It is an error to provide a credentials file and a transport instance. - transport = transports.SubscriberGrpcTransport( + # It is an error to provide a credentials file and a transport instance. + transport = transports.SubscriberGrpcTransport( credentials=ga_credentials.AnonymousCredentials(), ) with pytest.raises(ValueError): @@ -12047,34 +9497,3039 @@ def test_transport_get_channel(): assert channel -@pytest.mark.parametrize( - "transport_class", - [ - transports.SubscriberGrpcTransport, - transports.SubscriberGrpcAsyncIOTransport, - transports.SubscriberRestTransport, - ], -) -def test_transport_adc(transport_class): - # Test default credentials are used if not provided. - with mock.patch.object(google.auth, "default") as adc: - adc.return_value = (ga_credentials.AnonymousCredentials(), None) - transport_class() - adc.assert_called_once() +@pytest.mark.parametrize( + "transport_class", + [ + transports.SubscriberGrpcTransport, + transports.SubscriberGrpcAsyncIOTransport, + transports.SubscriberRestTransport, + ], +) +def test_transport_adc(transport_class): + # Test default credentials are used if not provided. + with mock.patch.object(google.auth, "default") as adc: + adc.return_value = (ga_credentials.AnonymousCredentials(), None) + transport_class() + adc.assert_called_once() + + +def test_transport_kind_grpc(): + transport = SubscriberClient.get_transport_class("grpc")( + credentials=ga_credentials.AnonymousCredentials() + ) + assert transport.kind == "grpc" + + +def test_initialize_client_w_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc" + ) + assert client is not None + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_create_subscription_empty_call_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.create_subscription), "__call__" + ) as call: + call.return_value = pubsub.Subscription() + client.create_subscription(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.Subscription() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_get_subscription_empty_call_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.get_subscription), "__call__") as call: + call.return_value = pubsub.Subscription() + client.get_subscription(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.GetSubscriptionRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_update_subscription_empty_call_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.update_subscription), "__call__" + ) as call: + call.return_value = pubsub.Subscription() + client.update_subscription(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.UpdateSubscriptionRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_list_subscriptions_empty_call_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.list_subscriptions), "__call__" + ) as call: + call.return_value = pubsub.ListSubscriptionsResponse() + client.list_subscriptions(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ListSubscriptionsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_delete_subscription_empty_call_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.delete_subscription), "__call__" + ) as call: + call.return_value = None + client.delete_subscription(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.DeleteSubscriptionRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_modify_ack_deadline_empty_call_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.modify_ack_deadline), "__call__" + ) as call: + call.return_value = None + client.modify_ack_deadline(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ModifyAckDeadlineRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_acknowledge_empty_call_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.acknowledge), "__call__") as call: + call.return_value = None + client.acknowledge(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.AcknowledgeRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_pull_empty_call_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.pull), "__call__") as call: + call.return_value = pubsub.PullResponse() + client.pull(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.PullRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_modify_push_config_empty_call_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.modify_push_config), "__call__" + ) as call: + call.return_value = None + client.modify_push_config(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ModifyPushConfigRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_get_snapshot_empty_call_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.get_snapshot), "__call__") as call: + call.return_value = pubsub.Snapshot() + client.get_snapshot(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.GetSnapshotRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_list_snapshots_empty_call_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.list_snapshots), "__call__") as call: + call.return_value = pubsub.ListSnapshotsResponse() + client.list_snapshots(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ListSnapshotsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_create_snapshot_empty_call_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.create_snapshot), "__call__") as call: + call.return_value = pubsub.Snapshot() + client.create_snapshot(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.CreateSnapshotRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_update_snapshot_empty_call_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.update_snapshot), "__call__") as call: + call.return_value = pubsub.Snapshot() + client.update_snapshot(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.UpdateSnapshotRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_delete_snapshot_empty_call_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.delete_snapshot), "__call__") as call: + call.return_value = None + client.delete_snapshot(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.DeleteSnapshotRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_seek_empty_call_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.seek), "__call__") as call: + call.return_value = pubsub.SeekResponse() + client.seek(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.SeekRequest() + + assert args[0] == request_msg + + +def test_transport_kind_grpc_asyncio(): + transport = SubscriberAsyncClient.get_transport_class("grpc_asyncio")( + credentials=async_anonymous_credentials() + ) + assert transport.kind == "grpc_asyncio" + + +def test_initialize_client_w_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), transport="grpc_asyncio" + ) + assert client is not None + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_create_subscription_empty_call_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.create_subscription), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Subscription( + name="name_value", + topic="topic_value", + ack_deadline_seconds=2066, + retain_acked_messages=True, + enable_message_ordering=True, + filter="filter_value", + detached=True, + enable_exactly_once_delivery=True, + state=pubsub.Subscription.State.ACTIVE, + ) + ) + await client.create_subscription(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.Subscription() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_get_subscription_empty_call_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.get_subscription), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Subscription( + name="name_value", + topic="topic_value", + ack_deadline_seconds=2066, + retain_acked_messages=True, + enable_message_ordering=True, + filter="filter_value", + detached=True, + enable_exactly_once_delivery=True, + state=pubsub.Subscription.State.ACTIVE, + ) + ) + await client.get_subscription(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.GetSubscriptionRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_update_subscription_empty_call_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.update_subscription), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Subscription( + name="name_value", + topic="topic_value", + ack_deadline_seconds=2066, + retain_acked_messages=True, + enable_message_ordering=True, + filter="filter_value", + detached=True, + enable_exactly_once_delivery=True, + state=pubsub.Subscription.State.ACTIVE, + ) + ) + await client.update_subscription(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.UpdateSubscriptionRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_list_subscriptions_empty_call_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.list_subscriptions), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.ListSubscriptionsResponse( + next_page_token="next_page_token_value", + ) + ) + await client.list_subscriptions(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ListSubscriptionsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_delete_subscription_empty_call_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.delete_subscription), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + await client.delete_subscription(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.DeleteSubscriptionRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_modify_ack_deadline_empty_call_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.modify_ack_deadline), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + await client.modify_ack_deadline(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ModifyAckDeadlineRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_acknowledge_empty_call_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.acknowledge), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + await client.acknowledge(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.AcknowledgeRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_pull_empty_call_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.pull), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(pubsub.PullResponse()) + await client.pull(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.PullRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_modify_push_config_empty_call_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.modify_push_config), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + await client.modify_push_config(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ModifyPushConfigRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_get_snapshot_empty_call_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.get_snapshot), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Snapshot( + name="name_value", + topic="topic_value", + ) + ) + await client.get_snapshot(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.GetSnapshotRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_list_snapshots_empty_call_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.list_snapshots), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.ListSnapshotsResponse( + next_page_token="next_page_token_value", + ) + ) + await client.list_snapshots(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ListSnapshotsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_create_snapshot_empty_call_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.create_snapshot), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Snapshot( + name="name_value", + topic="topic_value", + ) + ) + await client.create_snapshot(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.CreateSnapshotRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_update_snapshot_empty_call_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.update_snapshot), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + pubsub.Snapshot( + name="name_value", + topic="topic_value", + ) + ) + await client.update_snapshot(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.UpdateSnapshotRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_delete_snapshot_empty_call_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.delete_snapshot), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + await client.delete_snapshot(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.DeleteSnapshotRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_seek_empty_call_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.seek), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(pubsub.SeekResponse()) + await client.seek(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.SeekRequest() + + assert args[0] == request_msg + + +def test_transport_kind_rest(): + transport = SubscriberClient.get_transport_class("rest")( + credentials=ga_credentials.AnonymousCredentials() + ) + assert transport.kind == "rest" + + +def test_create_subscription_rest_bad_request(request_type=pubsub.Subscription): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.create_subscription(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.Subscription, + dict, + ], +) +def test_create_subscription_rest_call_success(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.Subscription( + name="name_value", + topic="topic_value", + ack_deadline_seconds=2066, + retain_acked_messages=True, + enable_message_ordering=True, + filter="filter_value", + detached=True, + enable_exactly_once_delivery=True, + state=pubsub.Subscription.State.ACTIVE, + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.Subscription.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.create_subscription(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pubsub.Subscription) + assert response.name == "name_value" + assert response.topic == "topic_value" + assert response.ack_deadline_seconds == 2066 + assert response.retain_acked_messages is True + assert response.enable_message_ordering is True + assert response.filter == "filter_value" + assert response.detached is True + assert response.enable_exactly_once_delivery is True + assert response.state == pubsub.Subscription.State.ACTIVE + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_create_subscription_rest_interceptors(null_interceptor): + transport = transports.SubscriberRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SubscriberRestInterceptor(), + ) + client = SubscriberClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SubscriberRestInterceptor, "post_create_subscription" + ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "pre_create_subscription" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.Subscription.pb(pubsub.Subscription()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.Subscription.to_json(pubsub.Subscription()) + req.return_value.content = return_value + + request = pubsub.Subscription() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.Subscription() + + client.create_subscription( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_get_subscription_rest_bad_request(request_type=pubsub.GetSubscriptionRequest): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.get_subscription(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.GetSubscriptionRequest, + dict, + ], +) +def test_get_subscription_rest_call_success(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.Subscription( + name="name_value", + topic="topic_value", + ack_deadline_seconds=2066, + retain_acked_messages=True, + enable_message_ordering=True, + filter="filter_value", + detached=True, + enable_exactly_once_delivery=True, + state=pubsub.Subscription.State.ACTIVE, + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.Subscription.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.get_subscription(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pubsub.Subscription) + assert response.name == "name_value" + assert response.topic == "topic_value" + assert response.ack_deadline_seconds == 2066 + assert response.retain_acked_messages is True + assert response.enable_message_ordering is True + assert response.filter == "filter_value" + assert response.detached is True + assert response.enable_exactly_once_delivery is True + assert response.state == pubsub.Subscription.State.ACTIVE + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_get_subscription_rest_interceptors(null_interceptor): + transport = transports.SubscriberRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SubscriberRestInterceptor(), + ) + client = SubscriberClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SubscriberRestInterceptor, "post_get_subscription" + ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "pre_get_subscription" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.GetSubscriptionRequest.pb(pubsub.GetSubscriptionRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.Subscription.to_json(pubsub.Subscription()) + req.return_value.content = return_value + + request = pubsub.GetSubscriptionRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.Subscription() + + client.get_subscription( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_update_subscription_rest_bad_request( + request_type=pubsub.UpdateSubscriptionRequest, +): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"subscription": {"name": "projects/sample1/subscriptions/sample2"}} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.update_subscription(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.UpdateSubscriptionRequest, + dict, + ], +) +def test_update_subscription_rest_call_success(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"subscription": {"name": "projects/sample1/subscriptions/sample2"}} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.Subscription( + name="name_value", + topic="topic_value", + ack_deadline_seconds=2066, + retain_acked_messages=True, + enable_message_ordering=True, + filter="filter_value", + detached=True, + enable_exactly_once_delivery=True, + state=pubsub.Subscription.State.ACTIVE, + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.Subscription.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.update_subscription(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pubsub.Subscription) + assert response.name == "name_value" + assert response.topic == "topic_value" + assert response.ack_deadline_seconds == 2066 + assert response.retain_acked_messages is True + assert response.enable_message_ordering is True + assert response.filter == "filter_value" + assert response.detached is True + assert response.enable_exactly_once_delivery is True + assert response.state == pubsub.Subscription.State.ACTIVE + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_update_subscription_rest_interceptors(null_interceptor): + transport = transports.SubscriberRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SubscriberRestInterceptor(), + ) + client = SubscriberClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SubscriberRestInterceptor, "post_update_subscription" + ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "pre_update_subscription" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.UpdateSubscriptionRequest.pb( + pubsub.UpdateSubscriptionRequest() + ) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.Subscription.to_json(pubsub.Subscription()) + req.return_value.content = return_value + + request = pubsub.UpdateSubscriptionRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.Subscription() + + client.update_subscription( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_list_subscriptions_rest_bad_request( + request_type=pubsub.ListSubscriptionsRequest, +): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"project": "projects/sample1"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.list_subscriptions(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.ListSubscriptionsRequest, + dict, + ], +) +def test_list_subscriptions_rest_call_success(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"project": "projects/sample1"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.ListSubscriptionsResponse( + next_page_token="next_page_token_value", + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.ListSubscriptionsResponse.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.list_subscriptions(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListSubscriptionsPager) + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_list_subscriptions_rest_interceptors(null_interceptor): + transport = transports.SubscriberRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SubscriberRestInterceptor(), + ) + client = SubscriberClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SubscriberRestInterceptor, "post_list_subscriptions" + ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "pre_list_subscriptions" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.ListSubscriptionsRequest.pb( + pubsub.ListSubscriptionsRequest() + ) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.ListSubscriptionsResponse.to_json( + pubsub.ListSubscriptionsResponse() + ) + req.return_value.content = return_value + + request = pubsub.ListSubscriptionsRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.ListSubscriptionsResponse() + + client.list_subscriptions( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_delete_subscription_rest_bad_request( + request_type=pubsub.DeleteSubscriptionRequest, +): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.delete_subscription(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.DeleteSubscriptionRequest, + dict, + ], +) +def test_delete_subscription_rest_call_success(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = None + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = "" + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.delete_subscription(request) + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_delete_subscription_rest_interceptors(null_interceptor): + transport = transports.SubscriberRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SubscriberRestInterceptor(), + ) + client = SubscriberClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SubscriberRestInterceptor, "pre_delete_subscription" + ) as pre: + pre.assert_not_called() + pb_message = pubsub.DeleteSubscriptionRequest.pb( + pubsub.DeleteSubscriptionRequest() + ) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + + request = pubsub.DeleteSubscriptionRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + + client.delete_subscription( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + + +def test_modify_ack_deadline_rest_bad_request( + request_type=pubsub.ModifyAckDeadlineRequest, +): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.modify_ack_deadline(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.ModifyAckDeadlineRequest, + dict, + ], +) +def test_modify_ack_deadline_rest_call_success(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = None + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = "" + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.modify_ack_deadline(request) + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_modify_ack_deadline_rest_interceptors(null_interceptor): + transport = transports.SubscriberRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SubscriberRestInterceptor(), + ) + client = SubscriberClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SubscriberRestInterceptor, "pre_modify_ack_deadline" + ) as pre: + pre.assert_not_called() + pb_message = pubsub.ModifyAckDeadlineRequest.pb( + pubsub.ModifyAckDeadlineRequest() + ) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + + request = pubsub.ModifyAckDeadlineRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + + client.modify_ack_deadline( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + + +def test_acknowledge_rest_bad_request(request_type=pubsub.AcknowledgeRequest): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.acknowledge(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.AcknowledgeRequest, + dict, + ], +) +def test_acknowledge_rest_call_success(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = None + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = "" + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.acknowledge(request) + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_acknowledge_rest_interceptors(null_interceptor): + transport = transports.SubscriberRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SubscriberRestInterceptor(), + ) + client = SubscriberClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SubscriberRestInterceptor, "pre_acknowledge" + ) as pre: + pre.assert_not_called() + pb_message = pubsub.AcknowledgeRequest.pb(pubsub.AcknowledgeRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + + request = pubsub.AcknowledgeRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + + client.acknowledge( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + + +def test_pull_rest_bad_request(request_type=pubsub.PullRequest): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.pull(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.PullRequest, + dict, + ], +) +def test_pull_rest_call_success(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.PullResponse() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.PullResponse.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.pull(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pubsub.PullResponse) + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_pull_rest_interceptors(null_interceptor): + transport = transports.SubscriberRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SubscriberRestInterceptor(), + ) + client = SubscriberClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SubscriberRestInterceptor, "post_pull" + ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "pre_pull" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.PullRequest.pb(pubsub.PullRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.PullResponse.to_json(pubsub.PullResponse()) + req.return_value.content = return_value + + request = pubsub.PullRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.PullResponse() + + client.pull( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_streaming_pull_rest_error(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + with pytest.raises(NotImplementedError) as not_implemented_error: + client.streaming_pull({}) + assert "Method StreamingPull is not available over REST transport" in str( + not_implemented_error.value + ) + + +def test_modify_push_config_rest_bad_request( + request_type=pubsub.ModifyPushConfigRequest, +): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.modify_push_config(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.ModifyPushConfigRequest, + dict, + ], +) +def test_modify_push_config_rest_call_success(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = None + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = "" + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.modify_push_config(request) + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_modify_push_config_rest_interceptors(null_interceptor): + transport = transports.SubscriberRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SubscriberRestInterceptor(), + ) + client = SubscriberClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SubscriberRestInterceptor, "pre_modify_push_config" + ) as pre: + pre.assert_not_called() + pb_message = pubsub.ModifyPushConfigRequest.pb(pubsub.ModifyPushConfigRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + + request = pubsub.ModifyPushConfigRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + + client.modify_push_config( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + + +def test_get_snapshot_rest_bad_request(request_type=pubsub.GetSnapshotRequest): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"snapshot": "projects/sample1/snapshots/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.get_snapshot(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.GetSnapshotRequest, + dict, + ], +) +def test_get_snapshot_rest_call_success(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"snapshot": "projects/sample1/snapshots/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.Snapshot( + name="name_value", + topic="topic_value", + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.Snapshot.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.get_snapshot(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pubsub.Snapshot) + assert response.name == "name_value" + assert response.topic == "topic_value" + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_get_snapshot_rest_interceptors(null_interceptor): + transport = transports.SubscriberRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SubscriberRestInterceptor(), + ) + client = SubscriberClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SubscriberRestInterceptor, "post_get_snapshot" + ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "pre_get_snapshot" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.GetSnapshotRequest.pb(pubsub.GetSnapshotRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.Snapshot.to_json(pubsub.Snapshot()) + req.return_value.content = return_value + + request = pubsub.GetSnapshotRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.Snapshot() + + client.get_snapshot( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_list_snapshots_rest_bad_request(request_type=pubsub.ListSnapshotsRequest): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"project": "projects/sample1"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.list_snapshots(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.ListSnapshotsRequest, + dict, + ], +) +def test_list_snapshots_rest_call_success(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"project": "projects/sample1"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.ListSnapshotsResponse( + next_page_token="next_page_token_value", + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.ListSnapshotsResponse.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.list_snapshots(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListSnapshotsPager) + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_list_snapshots_rest_interceptors(null_interceptor): + transport = transports.SubscriberRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SubscriberRestInterceptor(), + ) + client = SubscriberClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SubscriberRestInterceptor, "post_list_snapshots" + ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "pre_list_snapshots" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.ListSnapshotsRequest.pb(pubsub.ListSnapshotsRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.ListSnapshotsResponse.to_json( + pubsub.ListSnapshotsResponse() + ) + req.return_value.content = return_value + + request = pubsub.ListSnapshotsRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.ListSnapshotsResponse() + + client.list_snapshots( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_create_snapshot_rest_bad_request(request_type=pubsub.CreateSnapshotRequest): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/snapshots/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.create_snapshot(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.CreateSnapshotRequest, + dict, + ], +) +def test_create_snapshot_rest_call_success(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"name": "projects/sample1/snapshots/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.Snapshot( + name="name_value", + topic="topic_value", + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.Snapshot.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.create_snapshot(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pubsub.Snapshot) + assert response.name == "name_value" + assert response.topic == "topic_value" + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_create_snapshot_rest_interceptors(null_interceptor): + transport = transports.SubscriberRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SubscriberRestInterceptor(), + ) + client = SubscriberClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SubscriberRestInterceptor, "post_create_snapshot" + ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "pre_create_snapshot" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.CreateSnapshotRequest.pb(pubsub.CreateSnapshotRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.Snapshot.to_json(pubsub.Snapshot()) + req.return_value.content = return_value + + request = pubsub.CreateSnapshotRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.Snapshot() + + client.create_snapshot( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_update_snapshot_rest_bad_request(request_type=pubsub.UpdateSnapshotRequest): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"snapshot": {"name": "projects/sample1/snapshots/sample2"}} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.update_snapshot(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.UpdateSnapshotRequest, + dict, + ], +) +def test_update_snapshot_rest_call_success(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"snapshot": {"name": "projects/sample1/snapshots/sample2"}} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.Snapshot( + name="name_value", + topic="topic_value", + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.Snapshot.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.update_snapshot(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pubsub.Snapshot) + assert response.name == "name_value" + assert response.topic == "topic_value" + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_update_snapshot_rest_interceptors(null_interceptor): + transport = transports.SubscriberRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SubscriberRestInterceptor(), + ) + client = SubscriberClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SubscriberRestInterceptor, "post_update_snapshot" + ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "pre_update_snapshot" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.UpdateSnapshotRequest.pb(pubsub.UpdateSnapshotRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.Snapshot.to_json(pubsub.Snapshot()) + req.return_value.content = return_value + + request = pubsub.UpdateSnapshotRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.Snapshot() + + client.update_snapshot( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_delete_snapshot_rest_bad_request(request_type=pubsub.DeleteSnapshotRequest): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"snapshot": "projects/sample1/snapshots/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.delete_snapshot(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.DeleteSnapshotRequest, + dict, + ], +) +def test_delete_snapshot_rest_call_success(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"snapshot": "projects/sample1/snapshots/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = None + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = "" + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.delete_snapshot(request) + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_delete_snapshot_rest_interceptors(null_interceptor): + transport = transports.SubscriberRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SubscriberRestInterceptor(), + ) + client = SubscriberClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SubscriberRestInterceptor, "pre_delete_snapshot" + ) as pre: + pre.assert_not_called() + pb_message = pubsub.DeleteSnapshotRequest.pb(pubsub.DeleteSnapshotRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + + request = pubsub.DeleteSnapshotRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + + client.delete_snapshot( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + + +def test_seek_rest_bad_request(request_type=pubsub.SeekRequest): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + client.seek(request) + + +@pytest.mark.parametrize( + "request_type", + [ + pubsub.SeekRequest, + dict, + ], +) +def test_seek_rest_call_success(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + # send a request that will satisfy transcoding + request_init = {"subscription": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = pubsub.SeekResponse() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = pubsub.SeekResponse.pb(return_value) + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.seek(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, pubsub.SeekResponse) + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_seek_rest_interceptors(null_interceptor): + transport = transports.SubscriberRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.SubscriberRestInterceptor(), + ) + client = SubscriberClient(transport=transport) + + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + transports.SubscriberRestInterceptor, "post_seek" + ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "pre_seek" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = pubsub.SeekRequest.pb(pubsub.SeekRequest()) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = mock.Mock() + req.return_value.status_code = 200 + return_value = pubsub.SeekResponse.to_json(pubsub.SeekResponse()) + req.return_value.content = return_value + + request = pubsub.SeekRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = pubsub.SeekResponse() + + client.seek( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_get_iam_policy_rest_bad_request( + request_type=iam_policy_pb2.GetIamPolicyRequest, +): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type() + request = json_format.ParseDict( + {"resource": "projects/sample1/topics/sample2"}, request + ) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = Response() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + client.get_iam_policy(request) + + +@pytest.mark.parametrize( + "request_type", + [ + iam_policy_pb2.GetIamPolicyRequest, + dict, + ], +) +def test_get_iam_policy_rest(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + request_init = {"resource": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "request") as req: + # Designate an appropriate value for the returned response. + return_value = policy_pb2.Policy() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + + req.return_value = response_value + + response = client.get_iam_policy(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + + +def test_set_iam_policy_rest_bad_request( + request_type=iam_policy_pb2.SetIamPolicyRequest, +): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type() + request = json_format.ParseDict( + {"resource": "projects/sample1/topics/sample2"}, request + ) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = Response() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + client.set_iam_policy(request) + + +@pytest.mark.parametrize( + "request_type", + [ + iam_policy_pb2.SetIamPolicyRequest, + dict, + ], +) +def test_set_iam_policy_rest(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + request_init = {"resource": "projects/sample1/topics/sample2"} + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "request") as req: + # Designate an appropriate value for the returned response. + return_value = policy_pb2.Policy() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + + req.return_value = response_value + + response = client.set_iam_policy(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, policy_pb2.Policy) + + +def test_test_iam_permissions_rest_bad_request( + request_type=iam_policy_pb2.TestIamPermissionsRequest, +): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type() + request = json_format.ParseDict( + {"resource": "projects/sample1/subscriptions/sample2"}, request + ) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = Response() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + client.test_iam_permissions(request) + + +@pytest.mark.parametrize( + "request_type", + [ + iam_policy_pb2.TestIamPermissionsRequest, + dict, + ], +) +def test_test_iam_permissions_rest(request_type): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + request_init = {"resource": "projects/sample1/subscriptions/sample2"} + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "request") as req: + # Designate an appropriate value for the returned response. + return_value = iam_policy_pb2.TestIamPermissionsResponse() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = json_format.MessageToJson(return_value) + response_value.content = json_return_value.encode("UTF-8") + + req.return_value = response_value + + response = client.test_iam_permissions(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) + + +def test_initialize_client_w_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + assert client is not None + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_create_subscription_empty_call_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.create_subscription), "__call__" + ) as call: + client.create_subscription(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.Subscription() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_get_subscription_empty_call_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.get_subscription), "__call__") as call: + client.get_subscription(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.GetSubscriptionRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_update_subscription_empty_call_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.update_subscription), "__call__" + ) as call: + client.update_subscription(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.UpdateSubscriptionRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_list_subscriptions_empty_call_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.list_subscriptions), "__call__" + ) as call: + client.list_subscriptions(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ListSubscriptionsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_delete_subscription_empty_call_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.delete_subscription), "__call__" + ) as call: + client.delete_subscription(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.DeleteSubscriptionRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_modify_ack_deadline_empty_call_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.modify_ack_deadline), "__call__" + ) as call: + client.modify_ack_deadline(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ModifyAckDeadlineRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_acknowledge_empty_call_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.acknowledge), "__call__") as call: + client.acknowledge(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.AcknowledgeRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_pull_empty_call_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.pull), "__call__") as call: + client.pull(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.PullRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_modify_push_config_empty_call_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.modify_push_config), "__call__" + ) as call: + client.modify_push_config(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ModifyPushConfigRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_get_snapshot_empty_call_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.get_snapshot), "__call__") as call: + client.get_snapshot(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.GetSnapshotRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_list_snapshots_empty_call_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.list_snapshots), "__call__") as call: + client.list_snapshots(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.ListSnapshotsRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_create_snapshot_empty_call_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.create_snapshot), "__call__") as call: + client.create_snapshot(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.CreateSnapshotRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_update_snapshot_empty_call_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.update_snapshot), "__call__") as call: + client.update_snapshot(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.UpdateSnapshotRequest() + + assert args[0] == request_msg + + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_delete_snapshot_empty_call_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.delete_snapshot), "__call__") as call: + client.delete_snapshot(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.DeleteSnapshotRequest() + assert args[0] == request_msg -@pytest.mark.parametrize( - "transport_name", - [ - "grpc", - "rest", - ], -) -def test_transport_kind(transport_name): - transport = SubscriberClient.get_transport_class(transport_name)( + +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_seek_empty_call_rest(): + client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), + transport="rest", ) - assert transport.kind == transport_name + + # Mock the actual call, and fake the request. + with mock.patch.object(type(client.transport.seek), "__call__") as call: + client.seek(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = pubsub.SeekRequest() + + assert args[0] == request_msg def test_transport_grpc_default(): @@ -12767,194 +13222,6 @@ def test_client_with_default_client_info(): prep.assert_called_once_with(client_info) -@pytest.mark.asyncio -async def test_transport_close_async(): - client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="grpc_asyncio", - ) - with mock.patch.object( - type(getattr(client.transport, "grpc_channel")), "close" - ) as close: - async with client: - close.assert_not_called() - close.assert_called_once() - - -def test_get_iam_policy_rest_bad_request( - transport: str = "rest", request_type=iam_policy_pb2.GetIamPolicyRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - request = request_type() - request = json_format.ParseDict( - {"resource": "projects/sample1/topics/sample2"}, request - ) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.get_iam_policy(request) - - -@pytest.mark.parametrize( - "request_type", - [ - iam_policy_pb2.GetIamPolicyRequest, - dict, - ], -) -def test_get_iam_policy_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - request_init = {"resource": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = policy_pb2.Policy() - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - - response = client.get_iam_policy(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, policy_pb2.Policy) - - -def test_set_iam_policy_rest_bad_request( - transport: str = "rest", request_type=iam_policy_pb2.SetIamPolicyRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - request = request_type() - request = json_format.ParseDict( - {"resource": "projects/sample1/topics/sample2"}, request - ) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.set_iam_policy(request) - - -@pytest.mark.parametrize( - "request_type", - [ - iam_policy_pb2.SetIamPolicyRequest, - dict, - ], -) -def test_set_iam_policy_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - request_init = {"resource": "projects/sample1/topics/sample2"} - request = request_type(**request_init) - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = policy_pb2.Policy() - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - - response = client.set_iam_policy(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, policy_pb2.Policy) - - -def test_test_iam_permissions_rest_bad_request( - transport: str = "rest", request_type=iam_policy_pb2.TestIamPermissionsRequest -): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport=transport, - ) - - request = request_type() - request = json_format.ParseDict( - {"resource": "projects/sample1/subscriptions/sample2"}, request - ) - - # Mock the http request call within the method and fake a BadRequest error. - with mock.patch.object(Session, "request") as req, pytest.raises( - core_exceptions.BadRequest - ): - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 400 - response_value.request = Request() - req.return_value = response_value - client.test_iam_permissions(request) - - -@pytest.mark.parametrize( - "request_type", - [ - iam_policy_pb2.TestIamPermissionsRequest, - dict, - ], -) -def test_test_iam_permissions_rest(request_type): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), - transport="rest", - ) - request_init = {"resource": "projects/sample1/subscriptions/sample2"} - request = request_type(**request_init) - # Mock the http request call within the method and fake a response. - with mock.patch.object(type(client.transport._session), "request") as req: - # Designate an appropriate value for the returned response. - return_value = iam_policy_pb2.TestIamPermissionsResponse() - - # Wrap the value into a proper Response obj - response_value = Response() - response_value.status_code = 200 - json_return_value = json_format.MessageToJson(return_value) - - response_value._content = json_return_value.encode("UTF-8") - req.return_value = response_value - - response = client.test_iam_permissions(request) - - # Establish that the response is the type that we expect. - assert isinstance(response, iam_policy_pb2.TestIamPermissionsResponse) - - def test_set_iam_policy(transport: str = "grpc"): client = SubscriberClient( credentials=ga_credentials.AnonymousCredentials(), @@ -12990,7 +13257,7 @@ def test_set_iam_policy(transport: str = "grpc"): @pytest.mark.asyncio async def test_set_iam_policy_async(transport: str = "grpc_asyncio"): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -13055,7 +13322,7 @@ def test_set_iam_policy_field_headers(): @pytest.mark.asyncio async def test_set_iam_policy_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -13103,7 +13370,7 @@ def test_set_iam_policy_from_dict(): @pytest.mark.asyncio async def test_set_iam_policy_from_dict_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: @@ -13156,7 +13423,7 @@ def test_get_iam_policy(transport: str = "grpc"): @pytest.mark.asyncio async def test_get_iam_policy_async(transport: str = "grpc_asyncio"): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -13222,7 +13489,7 @@ def test_get_iam_policy_field_headers(): @pytest.mark.asyncio async def test_get_iam_policy_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -13270,7 +13537,7 @@ def test_get_iam_policy_from_dict(): @pytest.mark.asyncio async def test_get_iam_policy_from_dict_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: @@ -13322,7 +13589,7 @@ def test_test_iam_permissions(transport: str = "grpc"): @pytest.mark.asyncio async def test_test_iam_permissions_async(transport: str = "grpc_asyncio"): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), transport=transport, ) @@ -13389,7 +13656,7 @@ def test_test_iam_permissions_field_headers(): @pytest.mark.asyncio async def test_test_iam_permissions_field_headers_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Any value that is part of the HTTP/1.1 URI should be sent as @@ -13443,7 +13710,7 @@ def test_test_iam_permissions_from_dict(): @pytest.mark.asyncio async def test_test_iam_permissions_from_dict_async(): client = SubscriberAsyncClient( - credentials=ga_credentials.AnonymousCredentials(), + credentials=async_anonymous_credentials(), ) # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( @@ -13463,22 +13730,41 @@ async def test_test_iam_permissions_from_dict_async(): call.assert_called() -def test_transport_close(): - transports = { - "rest": "_session", - "grpc": "_grpc_channel", - } +def test_transport_close_grpc(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc" + ) + with mock.patch.object( + type(getattr(client.transport, "_grpc_channel")), "close" + ) as close: + with client: + close.assert_not_called() + close.assert_called_once() - for transport, close_name in transports.items(): - client = SubscriberClient( - credentials=ga_credentials.AnonymousCredentials(), transport=transport - ) - with mock.patch.object( - type(getattr(client.transport, close_name)), "close" - ) as close: - with client: - close.assert_not_called() - close.assert_called_once() + +@pytest.mark.asyncio +async def test_transport_close_grpc_asyncio(): + client = SubscriberAsyncClient( + credentials=async_anonymous_credentials(), transport="grpc_asyncio" + ) + with mock.patch.object( + type(getattr(client.transport, "_grpc_channel")), "close" + ) as close: + async with client: + close.assert_not_called() + close.assert_called_once() + + +def test_transport_close_rest(): + client = SubscriberClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + with mock.patch.object( + type(getattr(client.transport, "_session")), "close" + ) as close: + with client: + close.assert_not_called() + close.assert_called_once() def test_client_ctx(): From f2281f73406818ec41b1e210abac17195644602f Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Thu, 30 Jan 2025 15:35:33 -0800 Subject: [PATCH 132/190] chore(main): release 2.28.0 (#1351) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 13 +++++++++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f760fdd07..e0b4b7916 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.27.3" + ".": "2.28.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 69b1f0c27..63f40ce6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,19 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.28.0](https://github.com/googleapis/python-pubsub/compare/v2.27.3...v2.28.0) (2025-01-30) + + +### Features + +* Add support for message transforms to Topic and Subscription ([#1274](https://github.com/googleapis/python-pubsub/issues/1274)) ([e5e2f3f](https://github.com/googleapis/python-pubsub/commit/e5e2f3f732f451d14dfb4c37ae979e5c04045305)) + + +### Bug Fixes + +* Get channel target for a gRPC request ([#1339](https://github.com/googleapis/python-pubsub/issues/1339)) ([16ea766](https://github.com/googleapis/python-pubsub/commit/16ea76611d121700a3f3119d18919063d12c81c1)) +* Set creds only if transport not provided ([#1348](https://github.com/googleapis/python-pubsub/issues/1348)) ([59965a4](https://github.com/googleapis/python-pubsub/commit/59965a4804a434467a47815cdbdd5ce31bbb3662)) + ## [2.27.3](https://github.com/googleapis/python-pubsub/compare/v2.27.2...v2.27.3) (2025-01-24) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 03a0c3372..8f0f03c06 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.27.3" # {x-release-please-version} +__version__ = "2.28.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 03a0c3372..8f0f03c06 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.27.3" # {x-release-please-version} +__version__ = "2.28.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index d66015ac4..039f0ca3d 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "0.1.0" + "version": "2.28.0" }, "snippets": [ { From cb760a71cd4ad035d0c2c4c0f7b66bf52f18808c Mon Sep 17 00:00:00 2001 From: Lauren Huang Date: Fri, 31 Jan 2025 09:43:37 -0500 Subject: [PATCH 133/190] docs(samples): Increase example max_bytes setting for cloud storage subscriptions to encourage more performant subscribe (#1324) --- samples/snippets/subscriber.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/subscriber.py b/samples/snippets/subscriber.py index 180b091db..c09f5def1 100644 --- a/samples/snippets/subscriber.py +++ b/samples/snippets/subscriber.py @@ -557,7 +557,7 @@ def create_cloudstorage_subscription( # Min 1 minutes, max 10 minutes max_duration=max_duration, # Min 1 KB, max 10 GiB - max_bytes=2000, + max_bytes=10000000, ) # Wrap the subscriber in a 'with' block to automatically call close() to From 316883a2fec2ce142303aebd044805d165b29dcc Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Fri, 31 Jan 2025 16:32:53 +0100 Subject: [PATCH 134/190] chore(deps): update all dependencies (#1317) Co-authored-by: Anthonios Partheniou --- pytest.ini | 2 ++ samples/snippets/requirements-test.txt | 6 +++--- samples/snippets/requirements.txt | 12 ++++++------ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/pytest.ini b/pytest.ini index 4cedf2b4f..6d55a7315 100644 --- a/pytest.ini +++ b/pytest.ini @@ -18,3 +18,5 @@ filterwarnings = # Remove once the minimum supported version of googleapis-common-protos is 1.62.0 ignore:.*pkg_resources.declare_namespace:DeprecationWarning ignore:.*pkg_resources is deprecated as an API:DeprecationWarning + # Remove once https://github.com/googleapis/gapic-generator-python/issues/2303 is fixed + ignore:The python-bigquery library will stop supporting Python 3.7:PendingDeprecationWarning \ No newline at end of file diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index c705889ef..aa57a68a2 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1,7 +1,7 @@ backoff==2.2.1 pytest===7.4.4; python_version == '3.7' -pytest==8.3.2; python_version >= '3.8' +pytest==8.3.4; python_version >= '3.8' mock==5.1.0 flaky==3.8.1 -google-cloud-bigquery==3.25.0 -google-cloud-storage==2.18.2 +google-cloud-bigquery==3.27.0 +google-cloud-storage==2.19.0 diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 3a16ebc94..b348927be 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,10 +1,10 @@ -google-cloud-pubsub==2.26.0 +google-cloud-pubsub==2.27.1 avro==1.12.0 protobuf===4.24.4; python_version == '3.7' -protobuf==5.28.0; python_version >= '3.8' +protobuf==5.29.2; python_version >= '3.8' avro==1.12.0 -opentelemetry-api==1.22.0; python_version == '3.7' -opentelemetry-sdk==1.22.0; python_version == '3.7' -opentelemetry-api==1.27.0; python_version >= '3.8' -opentelemetry-sdk==1.27.0; python_version >= '3.8' +opentelemetry-api===1.22.0; python_version == '3.7' +opentelemetry-sdk===1.22.0; python_version == '3.7' +opentelemetry-api==1.29.0; python_version >= '3.8' +opentelemetry-sdk==1.29.0; python_version >= '3.8' opentelemetry-exporter-gcp-trace==1.7.0 From 820f986104ca39fd0c92ba6816319e939be1ed63 Mon Sep 17 00:00:00 2001 From: Mike Prieto Date: Mon, 10 Feb 2025 11:18:32 -0500 Subject: [PATCH 135/190] docs: Add samples and test for ingestion from Kafka sources (#1354) --- samples/snippets/noxfile.py | 2 +- samples/snippets/publisher.py | 209 +++++++++++++++++++++++++++++ samples/snippets/publisher_test.py | 117 ++++++++++++++++ samples/snippets/requirements.txt | 2 +- 4 files changed, 328 insertions(+), 2 deletions(-) diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index c9a3d1ecb..075047f97 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -124,7 +124,7 @@ def get_pytest_env_vars() -> Dict[str, str]: "--builtin=gettext", "--max-complexity=20", "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py", - "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202", + "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202,C901", "--max-line-length=88", ] diff --git a/samples/snippets/publisher.py b/samples/snippets/publisher.py index 7cb7ca223..270451511 100644 --- a/samples/snippets/publisher.py +++ b/samples/snippets/publisher.py @@ -185,6 +185,147 @@ def create_topic_with_cloud_storage_ingestion( # [END pubsub_create_topic_with_cloud_storage_ingestion] +def create_topic_with_aws_msk_ingestion( + project_id: str, + topic_id: str, + cluster_arn: str, + msk_topic: str, + aws_role_arn: str, + gcp_service_account: str, +) -> None: + """Create a new Pub/Sub topic with AWS MSK Ingestion Settings.""" + # [START pubsub_create_topic_with_aws_msk_ingestion] + from google.cloud import pubsub_v1 + from google.pubsub_v1.types import Topic + from google.pubsub_v1.types import IngestionDataSourceSettings + + # TODO(developer) + # project_id = "your-project-id" + # topic_id = "your-topic-id" + # cluster_arn = "your-cluster-arn" + # msk_topic = "your-msk-topic" + # aws_role_arn = "your-aws-role-arn" + # gcp_service_account = "your-gcp-service-account" + + publisher = pubsub_v1.PublisherClient() + topic_path = publisher.topic_path(project_id, topic_id) + + request = Topic( + name=topic_path, + ingestion_data_source_settings=IngestionDataSourceSettings( + aws_msk=IngestionDataSourceSettings.AwsMsk( + cluster_arn=cluster_arn, + topic=msk_topic, + aws_role_arn=aws_role_arn, + gcp_service_account=gcp_service_account, + ) + ), + ) + + topic = publisher.create_topic(request=request) + + print(f"Created topic: {topic.name} with AWS MSK Ingestion Settings") + # [END pubsub_create_topic_with_aws_msk_ingestion] + + +def create_topic_with_azure_event_hubs_ingestion( + project_id: str, + topic_id: str, + resource_group: str, + namespace: str, + event_hub: str, + client_id: str, + tenant_id: str, + subscription_id: str, + gcp_service_account: str, +) -> None: + """Create a new Pub/Sub topic with Azure Event Hubs Ingestion Settings.""" + # [START pubsub_create_topic_with_azure_event_hubs_ingestion] + from google.cloud import pubsub_v1 + from google.pubsub_v1.types import Topic + from google.pubsub_v1.types import IngestionDataSourceSettings + + # TODO(developer) + # project_id = "your-project-id" + # topic_id = "your-topic-id" + # resource_group = "your-resource-group" + # namespace = "your-namespace" + # event_hub = "your-event-hub" + # client_id = "your-client-id" + # tenant_id = "your-tenant-id" + # subscription_id = "your-subscription-id" + # gcp_service_account = "your-gcp-service-account" + + publisher = pubsub_v1.PublisherClient() + topic_path = publisher.topic_path(project_id, topic_id) + + request = Topic( + name=topic_path, + ingestion_data_source_settings=IngestionDataSourceSettings( + azure_event_hubs=IngestionDataSourceSettings.AzureEventHubs( + resource_group=resource_group, + namespace=namespace, + event_hub=event_hub, + client_id=client_id, + tenant_id=tenant_id, + subscription_id=subscription_id, + gcp_service_account=gcp_service_account, + ) + ), + ) + + topic = publisher.create_topic(request=request) + + print(f"Created topic: {topic.name} with Azure Event Hubs Ingestion Settings") + # [END pubsub_create_topic_with_azure_event_hubs_ingestion] + + +def create_topic_with_confluent_cloud_ingestion( + project_id: str, + topic_id: str, + bootstrap_server: str, + cluster_id: str, + confluent_topic: str, + identity_pool_id: str, + gcp_service_account: str, +) -> None: + """Create a new Pub/Sub topic with Confluent Cloud Ingestion Settings.""" + # [START pubsub_create_topic_with_confluent_cloud_ingestion] + from google.cloud import pubsub_v1 + from google.pubsub_v1.types import Topic + from google.pubsub_v1.types import IngestionDataSourceSettings + + # TODO(developer) + # project_id = "your-project-id" + # topic_id = "your-topic-id" + # bootstrap_server = "your-bootstrap-server" + # cluster_id = "your-cluster-id" + # confluent_topic = "your-confluent-topic" + # identity_pool_id = "your-identity-pool-id" + # gcp_service_account = "your-gcp-service-account" + + publisher = pubsub_v1.PublisherClient() + topic_path = publisher.topic_path(project_id, topic_id) + + request = Topic( + name=topic_path, + ingestion_data_source_settings=IngestionDataSourceSettings( + confluent_cloud=IngestionDataSourceSettings.ConfluentCloud( + bootstrap_server=bootstrap_server, + cluster_id=cluster_id, + topic=confluent_topic, + identity_pool_id=identity_pool_id, + gcp_service_account=gcp_service_account, + ) + ), + ) + + topic = publisher.create_topic(request=request) + + print(f"Created topic: {topic.name} with Confluent Cloud Ingestion Settings") + # [END pubsub_create_topic_with_confluent_cloud_ingestion] + + def update_topic_type( project_id: str, topic_id: str, @@ -710,6 +851,43 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: "minimum_object_create_time" ) + create_topic_with_aws_msk_ingestion_parser = subparsers.add_parser( + "create_aws_msk_ingestion", help=create_topic_with_aws_msk_ingestion.__doc__ + ) + create_topic_with_aws_msk_ingestion_parser.add_argument("topic_id") + create_topic_with_aws_msk_ingestion_parser.add_argument("cluster_arn") + create_topic_with_aws_msk_ingestion_parser.add_argument("msk_topic") + create_topic_with_aws_msk_ingestion_parser.add_argument("aws_role_arn") + create_topic_with_aws_msk_ingestion_parser.add_argument("gcp_service_account") + + create_topic_with_azure_event_hubs_ingestion_parser = subparsers.add_parser( + "create_azure_event_hubs_ingestion", + help=create_topic_with_azure_event_hubs_ingestion.__doc__, + ) + create_topic_with_azure_event_hubs_ingestion_parser.add_argument("topic_id") + create_topic_with_azure_event_hubs_ingestion_parser.add_argument("resource_group") + create_topic_with_azure_event_hubs_ingestion_parser.add_argument("namespace") + create_topic_with_azure_event_hubs_ingestion_parser.add_argument("event_hub") + create_topic_with_azure_event_hubs_ingestion_parser.add_argument("client_id") + create_topic_with_azure_event_hubs_ingestion_parser.add_argument("tenant_id") + create_topic_with_azure_event_hubs_ingestion_parser.add_argument("subscription_id") + create_topic_with_azure_event_hubs_ingestion_parser.add_argument( + "gcp_service_account" + ) + + create_topic_with_confluent_cloud_ingestion_parser = subparsers.add_parser( + "create_confluent_cloud_ingestion", + help=create_topic_with_confluent_cloud_ingestion.__doc__, + ) + create_topic_with_confluent_cloud_ingestion_parser.add_argument("topic_id") + create_topic_with_confluent_cloud_ingestion_parser.add_argument("bootstrap_server") + create_topic_with_confluent_cloud_ingestion_parser.add_argument("cluster_id") + create_topic_with_confluent_cloud_ingestion_parser.add_argument("confluent_topic") + create_topic_with_confluent_cloud_ingestion_parser.add_argument("identity_pool_id") + create_topic_with_confluent_cloud_ingestion_parser.add_argument( + "gcp_service_account" + ) + update_topic_type_parser = subparsers.add_parser( "update_kinesis_ingestion", help=update_topic_type.__doc__ ) @@ -798,6 +976,37 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: args.match_glob, args.minimum_object_create_time, ) + elif args.command == "create_aws_msk_ingestion": + create_topic_with_aws_msk_ingestion( + args.project_id, + args.topic_id, + args.cluster_arn, + args.msk_topic, + args.aws_role_arn, + args.gcp_service_account, + ) + elif args.command == "create_azure_event_hubs_ingestion": + create_topic_with_azure_event_hubs_ingestion( + args.project_id, + args.topic_id, + args.resource_group, + args.namespace, + args.event_hub, + args.client_id, + args.tenant_id, + args.subscription_id, + args.gcp_service_account, + ) + elif args.command == "create_confluent_cloud_ingestion": + create_topic_with_confluent_cloud_ingestion( + args.project_id, + args.topic_id, + args.bootstrap_server, + args.cluster_id, + args.confluent_topic, + args.identity_pool_id, + args.gcp_service_account, + ) elif args.command == "update_kinesis_ingestion": update_topic_type( args.project_id, diff --git a/samples/snippets/publisher_test.py b/samples/snippets/publisher_test.py index 6f17305cb..dc7b94027 100644 --- a/samples/snippets/publisher_test.py +++ b/samples/snippets/publisher_test.py @@ -196,6 +196,123 @@ def test_create_topic_with_cloud_storage_ingestion( publisher_client.delete_topic(request={"topic": topic_path}) +def test_create_topic_with_aws_msk_ingestion( + publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] +) -> None: + # The scope of `topic_path` is limited to this function. + topic_path = publisher_client.topic_path(PROJECT_ID, TOPIC_ID) + + # Outside of automated CI tests, these values must be of actual AWS resources for the test to pass. + cluster_arn = ( + "arn:aws:kafka:us-east-1:111111111111:cluster/fake-cluster-name/11111111-1111-1" + ) + msk_topic = "fake-msk-topic-name" + aws_role_arn = "arn:aws:iam::111111111111:role/fake-role-name" + gcp_service_account = ( + "fake-service-account@fake-gcp-project.iam.gserviceaccount.com" + ) + + try: + publisher_client.delete_topic(request={"topic": topic_path}) + except NotFound: + pass + + publisher.create_topic_with_aws_msk_ingestion( + PROJECT_ID, + TOPIC_ID, + cluster_arn, + msk_topic, + aws_role_arn, + gcp_service_account, + ) + + out, _ = capsys.readouterr() + assert f"Created topic: {topic_path} with AWS MSK Ingestion Settings" in out + + # Clean up resource created for the test. + publisher_client.delete_topic(request={"topic": topic_path}) + + +def test_create_topic_with_azure_event_hubs_ingestion( + publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] +) -> None: + # The scope of `topic_path` is limited to this function. + topic_path = publisher_client.topic_path(PROJECT_ID, TOPIC_ID) + + # Outside of automated CI tests, these values must be of actual Azure resources for the test to pass. + resource_group = "fake-resource-group" + namespace = "fake-namespace" + event_hub = "fake-event-hub" + client_id = "fake-client-id" + tenant_id = "fake-tenant-id" + subcription_id = "fake-subscription-id" + gcp_service_account = ( + "fake-service-account@fake-gcp-project.iam.gserviceaccount.com" + ) + + try: + publisher_client.delete_topic(request={"topic": topic_path}) + except NotFound: + pass + + publisher.create_topic_with_azure_event_hubs_ingestion( + PROJECT_ID, + TOPIC_ID, + resource_group, + namespace, + event_hub, + client_id, + tenant_id, + subcription_id, + gcp_service_account, + ) + + out, _ = capsys.readouterr() + assert ( + f"Created topic: {topic_path} with Azure Event Hubs Ingestion Settings" in out + ) + + # Clean up resource created for the test. + publisher_client.delete_topic(request={"topic": topic_path}) + + +def test_create_topic_with_confluent_cloud_ingestion( + publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] +) -> None: + # The scope of `topic_path` is limited to this function. + topic_path = publisher_client.topic_path(PROJECT_ID, TOPIC_ID) + + # Outside of automated CI tests, these values must be of actual Confluent resources for the test to pass. + bootstrap_server = "fake-bootstrap-server-id.us-south1.gcp.confluent.cloud:9092" + cluster_id = "fake-cluster-id" + confluent_topic = "fake-confluent-topic-name" + identity_pool_id = "fake-identity-pool-id" + gcp_service_account = ( + "fake-service-account@fake-gcp-project.iam.gserviceaccount.com" + ) + + try: + publisher_client.delete_topic(request={"topic": topic_path}) + except NotFound: + pass + + publisher.create_topic_with_confluent_cloud_ingestion( + PROJECT_ID, + TOPIC_ID, + bootstrap_server, + cluster_id, + confluent_topic, + identity_pool_id, + gcp_service_account, + ) + + out, _ = capsys.readouterr() + assert f"Created topic: {topic_path} with Confluent Cloud Ingestion Settings" in out + + # Clean up resource created for the test. + publisher_client.delete_topic(request={"topic": topic_path}) + + def test_update_topic_type( publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] ) -> None: diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index b348927be..b6ae767c6 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,4 +1,4 @@ -google-cloud-pubsub==2.27.1 +google-cloud-pubsub==2.28.0 avro==1.12.0 protobuf===4.24.4; python_version == '3.7' protobuf==5.29.2; python_version >= '3.8' From 06be31aa145ad29cb0c06ff31dc5ac58a1f4ae06 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 13:24:10 -0500 Subject: [PATCH 136/190] chore(python): conditionally load credentials in .kokoro/build.sh (#1364) Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- .github/.OwlBot.lock.yaml | 4 ++-- .kokoro/build.sh | 20 ++++++++++++++------ samples/snippets/noxfile.py | 2 +- samples/snippets/publisher.py | 2 +- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 4c0027ff1..3f7634f25 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:04c35dc5f49f0f503a306397d6d043685f8d2bb822ab515818c4208d7fb2db3a -# created: 2025-01-16T15:24:11.364245182Z + digest: sha256:f016446d6e520e5fb552c45b110cba3f217bffdd3d06bdddd076e9e6d13266cf +# created: 2025-02-21T19:32:52.01306189Z diff --git a/.kokoro/build.sh b/.kokoro/build.sh index 90e690e7a..d41b45aa1 100755 --- a/.kokoro/build.sh +++ b/.kokoro/build.sh @@ -15,11 +15,13 @@ set -eo pipefail +CURRENT_DIR=$(dirname "${BASH_SOURCE[0]}") + if [[ -z "${PROJECT_ROOT:-}" ]]; then - PROJECT_ROOT="github/python-pubsub" + PROJECT_ROOT=$(realpath "${CURRENT_DIR}/..") fi -cd "${PROJECT_ROOT}" +pushd "${PROJECT_ROOT}" # Disable buffering, so that the logs stream through. export PYTHONUNBUFFERED=1 @@ -28,10 +30,16 @@ export PYTHONUNBUFFERED=1 env | grep KOKORO # Setup service account credentials. -export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/service-account.json +if [[ -f "${KOKORO_GFILE_DIR}/service-account.json" ]] +then + export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/service-account.json +fi # Setup project id. -export PROJECT_ID=$(cat "${KOKORO_GFILE_DIR}/project-id.json") +if [[ -f "${KOKORO_GFILE_DIR}/project-id.json" ]] +then + export PROJECT_ID=$(cat "${KOKORO_GFILE_DIR}/project-id.json") +fi # If this is a continuous build, send the test log to the FlakyBot. # See https://github.com/googleapis/repo-automation-bots/tree/main/packages/flakybot. @@ -46,7 +54,7 @@ fi # If NOX_SESSION is set, it only runs the specified session, # otherwise run all the sessions. if [[ -n "${NOX_SESSION:-}" ]]; then - python3 -m nox -s ${NOX_SESSION:-} + python3 -m nox -s ${NOX_SESSION:-} else - python3 -m nox + python3 -m nox fi diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index 075047f97..c9a3d1ecb 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -124,7 +124,7 @@ def get_pytest_env_vars() -> Dict[str, str]: "--builtin=gettext", "--max-complexity=20", "--exclude=.nox,.cache,env,lib,generated_pb2,*_pb2.py,*_pb2_grpc.py", - "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202,C901", + "--ignore=E121,E123,E126,E203,E226,E24,E266,E501,E704,W503,W504,I202", "--max-line-length=88", ] diff --git a/samples/snippets/publisher.py b/samples/snippets/publisher.py index 270451511..e279324b8 100644 --- a/samples/snippets/publisher.py +++ b/samples/snippets/publisher.py @@ -809,7 +809,7 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: # [END pubsub_detach_subscription] -if __name__ == "__main__": +if __name__ == "__main__": # noqa: C901 parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, From 953ddb176f7e21fe48ea037c72237da39aadc1b7 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Thu, 6 Mar 2025 03:47:53 +0500 Subject: [PATCH 137/190] chore(revert): Revert "fix: get channel target for a gRPC request" (#1371) --- .../publisher/test_publisher_client.py | 32 ++++++++----------- .../subscriber/test_subscriber_client.py | 32 ++++++++----------- 2 files changed, 26 insertions(+), 38 deletions(-) diff --git a/tests/unit/pubsub_v1/publisher/test_publisher_client.py b/tests/unit/pubsub_v1/publisher/test_publisher_client.py index 1e1cc61b3..d1b7d4a81 100644 --- a/tests/unit/pubsub_v1/publisher/test_publisher_client.py +++ b/tests/unit/pubsub_v1/publisher/test_publisher_client.py @@ -57,12 +57,16 @@ typed_flaky = cast(Callable[[C], C], flaky(max_runs=5, min_passes=1)) -# NOTE: This interceptor is required to create an intercept channel. -class _PublisherClientGrpcInterceptor( - grpc.UnaryUnaryClientInterceptor, -): - def intercept_unary_unary(self, continuation, client_call_details, request): - pass +# Attempt to use `_thunk` to obtain the underlying grpc channel from +# the intercept channel. Default to obtaining the grpc channel directly +# for backwards compatibility. +# TODO(https://github.com/grpc/grpc/issues/38519): Workaround to obtain a channel +# until a public API is available. +def get_publish_channel(client): + try: + return client._transport.publish._thunk("")._channel + except AttributeError: + return client._transport.publish._channel def _assert_retries_equal(retry, retry2): @@ -424,27 +428,17 @@ def init(self, *args, **kwargs): assert client.transport._ssl_channel_credentials == mock_ssl_creds -def test_init_emulator(monkeypatch, creds): +def test_init_emulator(monkeypatch): monkeypatch.setenv("PUBSUB_EMULATOR_HOST", "/foo/bar:123") # NOTE: When the emulator host is set, a custom channel will be used, so # no credentials (mock ot otherwise) can be passed in. - - # TODO(https://github.com/grpc/grpc/issues/38519): Workaround to create an intercept - # channel (for forwards compatibility) with a channel created by the publisher client - # where target is set to the emulator host. - channel = publisher.Client().transport.grpc_channel - interceptor = _PublisherClientGrpcInterceptor() - intercept_channel = grpc.intercept_channel(channel, interceptor) - transport = publisher.Client.get_transport_class("grpc")( - credentials=creds, channel=intercept_channel - ) - client = publisher.Client(transport=transport) + client = publisher.Client() # Establish that a gRPC request would attempt to hit the emulator host. # # Sadly, there seems to be no good way to do this without poking at # the private API of gRPC. - channel = client._transport.publish._thunk("")._channel + channel = get_publish_channel(client) # Behavior to include dns prefix changed in gRPCv1.63 grpc_major, grpc_minor = [int(part) for part in grpc.__version__.split(".")[0:2]] if grpc_major > 1 or (grpc_major == 1 and grpc_minor >= 63): diff --git a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py index 4b381245d..3d3ff0111 100644 --- a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py +++ b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py @@ -36,12 +36,16 @@ from google.pubsub_v1.types import PubsubMessage -# NOTE: This interceptor is required to create an intercept channel. -class _SubscriberClientGrpcInterceptor( - grpc.UnaryUnaryClientInterceptor, -): - def intercept_unary_unary(self, continuation, client_call_details, request): - pass +# Attempt to use `_thunk` to obtain the underlying grpc channel from +# the intercept channel. Default to obtaining the grpc channel directly +# for backwards compatibility. +# TODO(https://github.com/grpc/grpc/issues/38519): Workaround to obtain a channel +# until a public API is available. +def get_pull_channel(client): + try: + return client._transport.pull._thunk("")._channel + except AttributeError: + return client._transport.pull._channel def test_init_default_client_info(creds): @@ -127,27 +131,17 @@ def init(self, *args, **kwargs): assert client.transport._ssl_channel_credentials == mock_ssl_creds -def test_init_emulator(monkeypatch, creds): +def test_init_emulator(monkeypatch): monkeypatch.setenv("PUBSUB_EMULATOR_HOST", "/baz/bacon:123") # NOTE: When the emulator host is set, a custom channel will be used, so # no credentials (mock ot otherwise) can be passed in. - - # TODO(https://github.com/grpc/grpc/issues/38519): Workaround to create an intercept - # channel (for forwards compatibility) with a channel created by the publisher client - # where target is set to the emulator host. - channel = subscriber.Client().transport.grpc_channel - interceptor = _SubscriberClientGrpcInterceptor() - intercept_channel = grpc.intercept_channel(channel, interceptor) - transport = subscriber.Client.get_transport_class("grpc")( - credentials=creds, channel=intercept_channel - ) - client = subscriber.Client(transport=transport) + client = subscriber.Client() # Establish that a gRPC request would attempt to hit the emulator host. # # Sadly, there seems to be no good way to do this without poking at # the private API of gRPC. - channel = client._transport.pull._thunk("")._channel + channel = get_pull_channel(client) # Behavior to include dns prefix changed in gRPCv1.63 grpc_major, grpc_minor = [int(part) for part in grpc.__version__.split(".")[0:2]] if grpc_major > 1 or (grpc_major == 1 and grpc_minor >= 63): From c364b15beb0bd09fe7cb3ad7dc7b7bad3ae6d2d7 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Mon, 10 Mar 2025 11:29:14 -0400 Subject: [PATCH 138/190] chore: remove unused files (#1368) --- .github/.OwlBot.lock.yaml | 4 +- .kokoro/docker/docs/Dockerfile | 89 ----- .kokoro/docker/docs/requirements.in | 2 - .kokoro/docker/docs/requirements.txt | 297 ---------------- .kokoro/docs/common.cfg | 66 ---- .kokoro/docs/docs-presubmit.cfg | 28 -- .kokoro/docs/docs.cfg | 1 - .kokoro/publish-docs.sh | 58 --- .kokoro/release.sh | 29 -- .kokoro/release/common.cfg | 43 --- .kokoro/release/release.cfg | 1 - .kokoro/requirements.in | 11 - .kokoro/requirements.txt | 509 --------------------------- 13 files changed, 2 insertions(+), 1136 deletions(-) delete mode 100644 .kokoro/docker/docs/Dockerfile delete mode 100644 .kokoro/docker/docs/requirements.in delete mode 100644 .kokoro/docker/docs/requirements.txt delete mode 100644 .kokoro/docs/common.cfg delete mode 100644 .kokoro/docs/docs-presubmit.cfg delete mode 100644 .kokoro/docs/docs.cfg delete mode 100755 .kokoro/publish-docs.sh delete mode 100755 .kokoro/release.sh delete mode 100644 .kokoro/release/common.cfg delete mode 100644 .kokoro/release/release.cfg delete mode 100644 .kokoro/requirements.in delete mode 100644 .kokoro/requirements.txt diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 3f7634f25..c631e1f7d 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:f016446d6e520e5fb552c45b110cba3f217bffdd3d06bdddd076e9e6d13266cf -# created: 2025-02-21T19:32:52.01306189Z + digest: sha256:5581906b957284864632cde4e9c51d1cc66b0094990b27e689132fe5cd036046 +# created: 2025-03-05 diff --git a/.kokoro/docker/docs/Dockerfile b/.kokoro/docker/docs/Dockerfile deleted file mode 100644 index e5410e296..000000000 --- a/.kokoro/docker/docs/Dockerfile +++ /dev/null @@ -1,89 +0,0 @@ -# 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. -# 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. - -from ubuntu:24.04 - -ENV DEBIAN_FRONTEND noninteractive - -# Ensure local Python is preferred over distribution Python. -ENV PATH /usr/local/bin:$PATH - -# Install dependencies. -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - apt-transport-https \ - build-essential \ - ca-certificates \ - curl \ - dirmngr \ - git \ - gpg-agent \ - graphviz \ - libbz2-dev \ - libdb5.3-dev \ - libexpat1-dev \ - libffi-dev \ - liblzma-dev \ - libreadline-dev \ - libsnappy-dev \ - libssl-dev \ - libsqlite3-dev \ - portaudio19-dev \ - redis-server \ - software-properties-common \ - ssh \ - sudo \ - tcl \ - tcl-dev \ - tk \ - tk-dev \ - uuid-dev \ - wget \ - zlib1g-dev \ - && add-apt-repository universe \ - && apt-get update \ - && apt-get -y install jq \ - && apt-get clean autoclean \ - && apt-get autoremove -y \ - && rm -rf /var/lib/apt/lists/* \ - && rm -f /var/cache/apt/archives/*.deb - - -###################### Install python 3.10.14 for docs/docfx session - -# Download python 3.10.14 -RUN wget https://www.python.org/ftp/python/3.10.14/Python-3.10.14.tgz - -# Extract files -RUN tar -xvf Python-3.10.14.tgz - -# Install python 3.10.14 -RUN ./Python-3.10.14/configure --enable-optimizations -RUN make altinstall - -ENV PATH /usr/local/bin/python3.10:$PATH - -###################### Install pip -RUN wget -O /tmp/get-pip.py 'https://bootstrap.pypa.io/get-pip.py' \ - && python3.10 /tmp/get-pip.py \ - && rm /tmp/get-pip.py - -# Test pip -RUN python3.10 -m pip - -# Install build requirements -COPY requirements.txt /requirements.txt -RUN python3.10 -m pip install --require-hashes -r requirements.txt - -CMD ["python3.10"] diff --git a/.kokoro/docker/docs/requirements.in b/.kokoro/docker/docs/requirements.in deleted file mode 100644 index 586bd0703..000000000 --- a/.kokoro/docker/docs/requirements.in +++ /dev/null @@ -1,2 +0,0 @@ -nox -gcp-docuploader diff --git a/.kokoro/docker/docs/requirements.txt b/.kokoro/docker/docs/requirements.txt deleted file mode 100644 index a9360a25b..000000000 --- a/.kokoro/docker/docs/requirements.txt +++ /dev/null @@ -1,297 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.10 -# by the following command: -# -# pip-compile --allow-unsafe --generate-hashes requirements.in -# -argcomplete==3.5.3 \ - --hash=sha256:2ab2c4a215c59fd6caaff41a869480a23e8f6a5f910b266c1808037f4e375b61 \ - --hash=sha256:c12bf50eded8aebb298c7b7da7a5ff3ee24dffd9f5281867dfe1424b58c55392 - # via nox -cachetools==5.5.0 \ - --hash=sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292 \ - --hash=sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a - # via google-auth -certifi==2024.12.14 \ - --hash=sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56 \ - --hash=sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db - # via requests -charset-normalizer==3.4.1 \ - --hash=sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537 \ - --hash=sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa \ - --hash=sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a \ - --hash=sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294 \ - --hash=sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b \ - --hash=sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd \ - --hash=sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601 \ - --hash=sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd \ - --hash=sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4 \ - --hash=sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d \ - --hash=sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2 \ - --hash=sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313 \ - --hash=sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd \ - --hash=sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa \ - --hash=sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8 \ - --hash=sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1 \ - --hash=sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2 \ - --hash=sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496 \ - --hash=sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d \ - --hash=sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b \ - --hash=sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e \ - --hash=sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a \ - --hash=sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4 \ - --hash=sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca \ - --hash=sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78 \ - --hash=sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408 \ - --hash=sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5 \ - --hash=sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3 \ - --hash=sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f \ - --hash=sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a \ - --hash=sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765 \ - --hash=sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6 \ - --hash=sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146 \ - --hash=sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6 \ - --hash=sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9 \ - --hash=sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd \ - --hash=sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c \ - --hash=sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f \ - --hash=sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545 \ - --hash=sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176 \ - --hash=sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770 \ - --hash=sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824 \ - --hash=sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f \ - --hash=sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf \ - --hash=sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487 \ - --hash=sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d \ - --hash=sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd \ - --hash=sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b \ - --hash=sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534 \ - --hash=sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f \ - --hash=sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b \ - --hash=sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9 \ - --hash=sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd \ - --hash=sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125 \ - --hash=sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9 \ - --hash=sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de \ - --hash=sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11 \ - --hash=sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d \ - --hash=sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35 \ - --hash=sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f \ - --hash=sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda \ - --hash=sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7 \ - --hash=sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a \ - --hash=sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971 \ - --hash=sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8 \ - --hash=sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41 \ - --hash=sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d \ - --hash=sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f \ - --hash=sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757 \ - --hash=sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a \ - --hash=sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886 \ - --hash=sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77 \ - --hash=sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76 \ - --hash=sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247 \ - --hash=sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85 \ - --hash=sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb \ - --hash=sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7 \ - --hash=sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e \ - --hash=sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6 \ - --hash=sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037 \ - --hash=sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1 \ - --hash=sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e \ - --hash=sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807 \ - --hash=sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407 \ - --hash=sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c \ - --hash=sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12 \ - --hash=sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3 \ - --hash=sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089 \ - --hash=sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd \ - --hash=sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e \ - --hash=sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00 \ - --hash=sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616 - # via requests -click==8.1.8 \ - --hash=sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2 \ - --hash=sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a - # via gcp-docuploader -colorlog==6.9.0 \ - --hash=sha256:5906e71acd67cb07a71e779c47c4bcb45fb8c2993eebe9e5adcd6a6f1b283eff \ - --hash=sha256:bfba54a1b93b94f54e1f4fe48395725a3d92fd2a4af702f6bd70946bdc0c6ac2 - # via - # gcp-docuploader - # nox -distlib==0.3.9 \ - --hash=sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87 \ - --hash=sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403 - # via virtualenv -filelock==3.16.1 \ - --hash=sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0 \ - --hash=sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435 - # via virtualenv -gcp-docuploader==0.6.5 \ - --hash=sha256:30221d4ac3e5a2b9c69aa52fdbef68cc3f27d0e6d0d90e220fc024584b8d2318 \ - --hash=sha256:b7458ef93f605b9d46a4bf3a8dc1755dad1f31d030c8679edf304e343b347eea - # via -r requirements.in -google-api-core==2.24.0 \ - --hash=sha256:10d82ac0fca69c82a25b3efdeefccf6f28e02ebb97925a8cce8edbfe379929d9 \ - --hash=sha256:e255640547a597a4da010876d333208ddac417d60add22b6851a0c66a831fcaf - # via - # google-cloud-core - # google-cloud-storage -google-auth==2.37.0 \ - --hash=sha256:0054623abf1f9c83492c63d3f47e77f0a544caa3d40b2d98e099a611c2dd5d00 \ - --hash=sha256:42664f18290a6be591be5329a96fe30184be1a1badb7292a7f686a9659de9ca0 - # via - # google-api-core - # google-cloud-core - # google-cloud-storage -google-cloud-core==2.4.1 \ - --hash=sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073 \ - --hash=sha256:a9e6a4422b9ac5c29f79a0ede9485473338e2ce78d91f2370c01e730eab22e61 - # via google-cloud-storage -google-cloud-storage==2.19.0 \ - --hash=sha256:aeb971b5c29cf8ab98445082cbfe7b161a1f48ed275822f59ed3f1524ea54fba \ - --hash=sha256:cd05e9e7191ba6cb68934d8eb76054d9be4562aa89dbc4236feee4d7d51342b2 - # via gcp-docuploader -google-crc32c==1.6.0 \ - --hash=sha256:05e2d8c9a2f853ff116db9706b4a27350587f341eda835f46db3c0a8c8ce2f24 \ - --hash=sha256:18e311c64008f1f1379158158bb3f0c8d72635b9eb4f9545f8cf990c5668e59d \ - --hash=sha256:236c87a46cdf06384f614e9092b82c05f81bd34b80248021f729396a78e55d7e \ - --hash=sha256:35834855408429cecf495cac67ccbab802de269e948e27478b1e47dfb6465e57 \ - --hash=sha256:386122eeaaa76951a8196310432c5b0ef3b53590ef4c317ec7588ec554fec5d2 \ - --hash=sha256:40b05ab32a5067525670880eb5d169529089a26fe35dce8891127aeddc1950e8 \ - --hash=sha256:48abd62ca76a2cbe034542ed1b6aee851b6f28aaca4e6551b5599b6f3ef175cc \ - --hash=sha256:50cf2a96da226dcbff8671233ecf37bf6e95de98b2a2ebadbfdf455e6d05df42 \ - --hash=sha256:51c4f54dd8c6dfeb58d1df5e4f7f97df8abf17a36626a217f169893d1d7f3e9f \ - --hash=sha256:5bcc90b34df28a4b38653c36bb5ada35671ad105c99cfe915fb5bed7ad6924aa \ - --hash=sha256:62f6d4a29fea082ac4a3c9be5e415218255cf11684ac6ef5488eea0c9132689b \ - --hash=sha256:6eceb6ad197656a1ff49ebfbbfa870678c75be4344feb35ac1edf694309413dc \ - --hash=sha256:7aec8e88a3583515f9e0957fe4f5f6d8d4997e36d0f61624e70469771584c760 \ - --hash=sha256:91ca8145b060679ec9176e6de4f89b07363d6805bd4760631ef254905503598d \ - --hash=sha256:a184243544811e4a50d345838a883733461e67578959ac59964e43cca2c791e7 \ - --hash=sha256:a9e4b426c3702f3cd23b933436487eb34e01e00327fac20c9aebb68ccf34117d \ - --hash=sha256:bb0966e1c50d0ef5bc743312cc730b533491d60585a9a08f897274e57c3f70e0 \ - --hash=sha256:bb8b3c75bd157010459b15222c3fd30577042a7060e29d42dabce449c087f2b3 \ - --hash=sha256:bd5e7d2445d1a958c266bfa5d04c39932dc54093fa391736dbfdb0f1929c1fb3 \ - --hash=sha256:c87d98c7c4a69066fd31701c4e10d178a648c2cac3452e62c6b24dc51f9fcc00 \ - --hash=sha256:d2952396dc604544ea7476b33fe87faedc24d666fb0c2d5ac971a2b9576ab871 \ - --hash=sha256:d8797406499f28b5ef791f339594b0b5fdedf54e203b5066675c406ba69d705c \ - --hash=sha256:d9e9913f7bd69e093b81da4535ce27af842e7bf371cde42d1ae9e9bd382dc0e9 \ - --hash=sha256:e2806553238cd076f0a55bddab37a532b53580e699ed8e5606d0de1f856b5205 \ - --hash=sha256:ebab974b1687509e5c973b5c4b8b146683e101e102e17a86bd196ecaa4d099fc \ - --hash=sha256:ed767bf4ba90104c1216b68111613f0d5926fb3780660ea1198fc469af410e9d \ - --hash=sha256:f7a1fc29803712f80879b0806cb83ab24ce62fc8daf0569f2204a0cfd7f68ed4 - # via - # google-cloud-storage - # google-resumable-media -google-resumable-media==2.7.2 \ - --hash=sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa \ - --hash=sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0 - # via google-cloud-storage -googleapis-common-protos==1.66.0 \ - --hash=sha256:c3e7b33d15fdca5374cc0a7346dd92ffa847425cc4ea941d970f13680052ec8c \ - --hash=sha256:d7abcd75fabb2e0ec9f74466401f6c119a0b498e27370e9be4c94cb7e382b8ed - # via google-api-core -idna==3.10 \ - --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ - --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 - # via requests -nox==2024.10.9 \ - --hash=sha256:1d36f309a0a2a853e9bccb76bbef6bb118ba92fa92674d15604ca99adeb29eab \ - --hash=sha256:7aa9dc8d1c27e9f45ab046ffd1c3b2c4f7c91755304769df231308849ebded95 - # via -r requirements.in -packaging==24.2 \ - --hash=sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759 \ - --hash=sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f - # via nox -platformdirs==4.3.6 \ - --hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \ - --hash=sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb - # via virtualenv -proto-plus==1.25.0 \ - --hash=sha256:c91fc4a65074ade8e458e95ef8bac34d4008daa7cce4a12d6707066fca648961 \ - --hash=sha256:fbb17f57f7bd05a68b7707e745e26528b0b3c34e378db91eef93912c54982d91 - # via google-api-core -protobuf==5.29.3 \ - --hash=sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f \ - --hash=sha256:0eb32bfa5219fc8d4111803e9a690658aa2e6366384fd0851064b963b6d1f2a7 \ - --hash=sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888 \ - --hash=sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620 \ - --hash=sha256:6ce8cc3389a20693bfde6c6562e03474c40851b44975c9b2bf6df7d8c4f864da \ - --hash=sha256:84a57163a0ccef3f96e4b6a20516cedcf5bb3a95a657131c5c3ac62200d23252 \ - --hash=sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a \ - --hash=sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e \ - --hash=sha256:b89c115d877892a512f79a8114564fb435943b59067615894c3b13cd3e1fa107 \ - --hash=sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f \ - --hash=sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84 - # via - # gcp-docuploader - # google-api-core - # googleapis-common-protos - # proto-plus -pyasn1==0.6.1 \ - --hash=sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629 \ - --hash=sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034 - # via - # pyasn1-modules - # rsa -pyasn1-modules==0.4.1 \ - --hash=sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd \ - --hash=sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c - # via google-auth -requests==2.32.3 \ - --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \ - --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6 - # via - # google-api-core - # google-cloud-storage -rsa==4.9 \ - --hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \ - --hash=sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21 - # via google-auth -six==1.17.0 \ - --hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \ - --hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 - # via gcp-docuploader -tomli==2.2.1 \ - --hash=sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6 \ - --hash=sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd \ - --hash=sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c \ - --hash=sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b \ - --hash=sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8 \ - --hash=sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6 \ - --hash=sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77 \ - --hash=sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff \ - --hash=sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea \ - --hash=sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192 \ - --hash=sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249 \ - --hash=sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee \ - --hash=sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4 \ - --hash=sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98 \ - --hash=sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8 \ - --hash=sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4 \ - --hash=sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281 \ - --hash=sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744 \ - --hash=sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69 \ - --hash=sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13 \ - --hash=sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140 \ - --hash=sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e \ - --hash=sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e \ - --hash=sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc \ - --hash=sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff \ - --hash=sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec \ - --hash=sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2 \ - --hash=sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222 \ - --hash=sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106 \ - --hash=sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272 \ - --hash=sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a \ - --hash=sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7 - # via nox -urllib3==2.3.0 \ - --hash=sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df \ - --hash=sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d - # via requests -virtualenv==20.28.1 \ - --hash=sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb \ - --hash=sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329 - # via nox diff --git a/.kokoro/docs/common.cfg b/.kokoro/docs/common.cfg deleted file mode 100644 index a9392e09a..000000000 --- a/.kokoro/docs/common.cfg +++ /dev/null @@ -1,66 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "python-pubsub/.kokoro/trampoline_v2.sh" - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-lib-docs" -} -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-pubsub/.kokoro/publish-docs.sh" -} - -env_vars: { - key: "STAGING_BUCKET" - value: "docs-staging" -} - -env_vars: { - key: "V2_STAGING_BUCKET" - # Push google cloud library docs to the Cloud RAD bucket `docs-staging-v2` - value: "docs-staging-v2" -} - -# It will upload the docker image after successful builds. -env_vars: { - key: "TRAMPOLINE_IMAGE_UPLOAD" - value: "true" -} - -# It will always build the docker image. -env_vars: { - key: "TRAMPOLINE_DOCKERFILE" - value: ".kokoro/docker/docs/Dockerfile" -} - -# Fetch the token needed for reporting release status to GitHub -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "yoshi-automation-github-key" - } - } -} - -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "docuploader_service_account" - } - } -} diff --git a/.kokoro/docs/docs-presubmit.cfg b/.kokoro/docs/docs-presubmit.cfg deleted file mode 100644 index 2c532d9db..000000000 --- a/.kokoro/docs/docs-presubmit.cfg +++ /dev/null @@ -1,28 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "STAGING_BUCKET" - value: "gcloud-python-test" -} - -env_vars: { - key: "V2_STAGING_BUCKET" - value: "gcloud-python-test" -} - -# We only upload the image in the main `docs` build. -env_vars: { - key: "TRAMPOLINE_IMAGE_UPLOAD" - value: "false" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-pubsub/.kokoro/build.sh" -} - -# Only run this nox session. -env_vars: { - key: "NOX_SESSION" - value: "docs docfx" -} diff --git a/.kokoro/docs/docs.cfg b/.kokoro/docs/docs.cfg deleted file mode 100644 index 8f43917d9..000000000 --- a/.kokoro/docs/docs.cfg +++ /dev/null @@ -1 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto \ No newline at end of file diff --git a/.kokoro/publish-docs.sh b/.kokoro/publish-docs.sh deleted file mode 100755 index 4ed4aaf13..000000000 --- a/.kokoro/publish-docs.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash -# 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. -# 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. - -set -eo pipefail - -# Disable buffering, so that the logs stream through. -export PYTHONUNBUFFERED=1 - -export PATH="${HOME}/.local/bin:${PATH}" - -# build docs -nox -s docs - -# create metadata -python3.10 -m docuploader create-metadata \ - --name=$(jq --raw-output '.name // empty' .repo-metadata.json) \ - --version=$(python3.10 setup.py --version) \ - --language=$(jq --raw-output '.language // empty' .repo-metadata.json) \ - --distribution-name=$(python3.10 setup.py --name) \ - --product-page=$(jq --raw-output '.product_documentation // empty' .repo-metadata.json) \ - --github-repository=$(jq --raw-output '.repo // empty' .repo-metadata.json) \ - --issue-tracker=$(jq --raw-output '.issue_tracker // empty' .repo-metadata.json) - -cat docs.metadata - -# upload docs -python3.10 -m docuploader upload docs/_build/html --metadata-file docs.metadata --staging-bucket "${STAGING_BUCKET}" - - -# docfx yaml files -nox -s docfx - -# create metadata. -python3.10 -m docuploader create-metadata \ - --name=$(jq --raw-output '.name // empty' .repo-metadata.json) \ - --version=$(python3.10 setup.py --version) \ - --language=$(jq --raw-output '.language // empty' .repo-metadata.json) \ - --distribution-name=$(python3.10 setup.py --name) \ - --product-page=$(jq --raw-output '.product_documentation // empty' .repo-metadata.json) \ - --github-repository=$(jq --raw-output '.repo // empty' .repo-metadata.json) \ - --issue-tracker=$(jq --raw-output '.issue_tracker // empty' .repo-metadata.json) - -cat docs.metadata - -# upload docs -python3.10 -m docuploader upload docs/_build/html/docfx_yaml --metadata-file docs.metadata --destination-prefix docfx --staging-bucket "${V2_STAGING_BUCKET}" diff --git a/.kokoro/release.sh b/.kokoro/release.sh deleted file mode 100755 index 006893576..000000000 --- a/.kokoro/release.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# 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. -# 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. - -set -eo pipefail - -# Start the releasetool reporter -python3 -m pip install --require-hashes -r github/python-pubsub/.kokoro/requirements.txt -python3 -m releasetool publish-reporter-script > /tmp/publisher-script; source /tmp/publisher-script - -# Disable buffering, so that the logs stream through. -export PYTHONUNBUFFERED=1 - -# Move into the package, build the distribution and upload. -TWINE_PASSWORD=$(cat "${KOKORO_KEYSTORE_DIR}/73713_google-cloud-pypi-token-keystore-3") -cd github/python-pubsub -python3 setup.py sdist bdist_wheel -twine upload --username __token__ --password "${TWINE_PASSWORD}" dist/* diff --git a/.kokoro/release/common.cfg b/.kokoro/release/common.cfg deleted file mode 100644 index a6b92c637..000000000 --- a/.kokoro/release/common.cfg +++ /dev/null @@ -1,43 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "python-pubsub/.kokoro/trampoline.sh" - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-multi" -} -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-pubsub/.kokoro/release.sh" -} - -# Fetch PyPI password -before_action { - fetch_keystore { - keystore_resource { - keystore_config_id: 73713 - keyname: "google-cloud-pypi-token-keystore-3" - } - } -} - -# Store the packages we uploaded to PyPI. That way, we have a record of exactly -# what we published, which we can use to generate SBOMs and attestations. -action { - define_artifacts { - regex: "github/python-pubsub/**/*.tar.gz" - strip_prefix: "github/python-pubsub" - } -} diff --git a/.kokoro/release/release.cfg b/.kokoro/release/release.cfg deleted file mode 100644 index 8f43917d9..000000000 --- a/.kokoro/release/release.cfg +++ /dev/null @@ -1 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto \ No newline at end of file diff --git a/.kokoro/requirements.in b/.kokoro/requirements.in deleted file mode 100644 index fff4d9ce0..000000000 --- a/.kokoro/requirements.in +++ /dev/null @@ -1,11 +0,0 @@ -gcp-docuploader -gcp-releasetool>=2 # required for compatibility with cryptography>=42.x -importlib-metadata -typing-extensions -twine -wheel -setuptools -nox>=2022.11.21 # required to remove dependency on py -charset-normalizer<3 -click<8.1.0 -cryptography>=42.0.5 diff --git a/.kokoro/requirements.txt b/.kokoro/requirements.txt deleted file mode 100644 index 006d8ef93..000000000 --- a/.kokoro/requirements.txt +++ /dev/null @@ -1,509 +0,0 @@ -# -# This file is autogenerated by pip-compile with Python 3.9 -# by the following command: -# -# pip-compile --allow-unsafe --generate-hashes requirements.in -# -argcomplete==3.5.1 \ - --hash=sha256:1a1d148bdaa3e3b93454900163403df41448a248af01b6e849edc5ac08e6c363 \ - --hash=sha256:eb1ee355aa2557bd3d0145de7b06b2a45b0ce461e1e7813f5d066039ab4177b4 - # via nox -attrs==24.2.0 \ - --hash=sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346 \ - --hash=sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2 - # via gcp-releasetool -backports-tarfile==1.2.0 \ - --hash=sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34 \ - --hash=sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991 - # via jaraco-context -cachetools==5.5.0 \ - --hash=sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292 \ - --hash=sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a - # via google-auth -certifi==2024.8.30 \ - --hash=sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 \ - --hash=sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9 - # via requests -cffi==1.17.1 \ - --hash=sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8 \ - --hash=sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2 \ - --hash=sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1 \ - --hash=sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15 \ - --hash=sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36 \ - --hash=sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824 \ - --hash=sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8 \ - --hash=sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36 \ - --hash=sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17 \ - --hash=sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf \ - --hash=sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc \ - --hash=sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3 \ - --hash=sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed \ - --hash=sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702 \ - --hash=sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1 \ - --hash=sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8 \ - --hash=sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903 \ - --hash=sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6 \ - --hash=sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d \ - --hash=sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b \ - --hash=sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e \ - --hash=sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be \ - --hash=sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c \ - --hash=sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683 \ - --hash=sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9 \ - --hash=sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c \ - --hash=sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8 \ - --hash=sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1 \ - --hash=sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4 \ - --hash=sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655 \ - --hash=sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67 \ - --hash=sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595 \ - --hash=sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0 \ - --hash=sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65 \ - --hash=sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41 \ - --hash=sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6 \ - --hash=sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401 \ - --hash=sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6 \ - --hash=sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3 \ - --hash=sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16 \ - --hash=sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93 \ - --hash=sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e \ - --hash=sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4 \ - --hash=sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964 \ - --hash=sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c \ - --hash=sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576 \ - --hash=sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0 \ - --hash=sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3 \ - --hash=sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662 \ - --hash=sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3 \ - --hash=sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff \ - --hash=sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5 \ - --hash=sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd \ - --hash=sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f \ - --hash=sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5 \ - --hash=sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14 \ - --hash=sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d \ - --hash=sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9 \ - --hash=sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7 \ - --hash=sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382 \ - --hash=sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a \ - --hash=sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e \ - --hash=sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a \ - --hash=sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4 \ - --hash=sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99 \ - --hash=sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87 \ - --hash=sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b - # via cryptography -charset-normalizer==2.1.1 \ - --hash=sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845 \ - --hash=sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f - # via - # -r requirements.in - # requests -click==8.0.4 \ - --hash=sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1 \ - --hash=sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb - # via - # -r requirements.in - # gcp-docuploader - # gcp-releasetool -colorlog==6.8.2 \ - --hash=sha256:3e3e079a41feb5a1b64f978b5ea4f46040a94f11f0e8bbb8261e3dbbeca64d44 \ - --hash=sha256:4dcbb62368e2800cb3c5abd348da7e53f6c362dda502ec27c560b2e58a66bd33 - # via - # gcp-docuploader - # nox -cryptography==43.0.1 \ - --hash=sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494 \ - --hash=sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806 \ - --hash=sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d \ - --hash=sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062 \ - --hash=sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2 \ - --hash=sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4 \ - --hash=sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1 \ - --hash=sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85 \ - --hash=sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84 \ - --hash=sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042 \ - --hash=sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d \ - --hash=sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962 \ - --hash=sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2 \ - --hash=sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa \ - --hash=sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d \ - --hash=sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365 \ - --hash=sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96 \ - --hash=sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47 \ - --hash=sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d \ - --hash=sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d \ - --hash=sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c \ - --hash=sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb \ - --hash=sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277 \ - --hash=sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172 \ - --hash=sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034 \ - --hash=sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a \ - --hash=sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289 - # via - # -r requirements.in - # gcp-releasetool - # secretstorage -distlib==0.3.9 \ - --hash=sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87 \ - --hash=sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403 - # via virtualenv -docutils==0.21.2 \ - --hash=sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f \ - --hash=sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2 - # via readme-renderer -filelock==3.16.1 \ - --hash=sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0 \ - --hash=sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435 - # via virtualenv -gcp-docuploader==0.6.5 \ - --hash=sha256:30221d4ac3e5a2b9c69aa52fdbef68cc3f27d0e6d0d90e220fc024584b8d2318 \ - --hash=sha256:b7458ef93f605b9d46a4bf3a8dc1755dad1f31d030c8679edf304e343b347eea - # via -r requirements.in -gcp-releasetool==2.1.1 \ - --hash=sha256:25639269f4eae510094f9dbed9894977e1966933211eb155a451deebc3fc0b30 \ - --hash=sha256:845f4ded3d9bfe8cc7fdaad789e83f4ea014affa77785259a7ddac4b243e099e - # via -r requirements.in -google-api-core==2.21.0 \ - --hash=sha256:4a152fd11a9f774ea606388d423b68aa7e6d6a0ffe4c8266f74979613ec09f81 \ - --hash=sha256:6869eacb2a37720380ba5898312af79a4d30b8bca1548fb4093e0697dc4bdf5d - # via - # google-cloud-core - # google-cloud-storage -google-auth==2.35.0 \ - --hash=sha256:25df55f327ef021de8be50bad0dfd4a916ad0de96da86cd05661c9297723ad3f \ - --hash=sha256:f4c64ed4e01e8e8b646ef34c018f8bf3338df0c8e37d8b3bba40e7f574a3278a - # via - # gcp-releasetool - # google-api-core - # google-cloud-core - # google-cloud-storage -google-cloud-core==2.4.1 \ - --hash=sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073 \ - --hash=sha256:a9e6a4422b9ac5c29f79a0ede9485473338e2ce78d91f2370c01e730eab22e61 - # via google-cloud-storage -google-cloud-storage==2.18.2 \ - --hash=sha256:97a4d45c368b7d401ed48c4fdfe86e1e1cb96401c9e199e419d289e2c0370166 \ - --hash=sha256:aaf7acd70cdad9f274d29332673fcab98708d0e1f4dceb5a5356aaef06af4d99 - # via gcp-docuploader -google-crc32c==1.6.0 \ - --hash=sha256:05e2d8c9a2f853ff116db9706b4a27350587f341eda835f46db3c0a8c8ce2f24 \ - --hash=sha256:18e311c64008f1f1379158158bb3f0c8d72635b9eb4f9545f8cf990c5668e59d \ - --hash=sha256:236c87a46cdf06384f614e9092b82c05f81bd34b80248021f729396a78e55d7e \ - --hash=sha256:35834855408429cecf495cac67ccbab802de269e948e27478b1e47dfb6465e57 \ - --hash=sha256:386122eeaaa76951a8196310432c5b0ef3b53590ef4c317ec7588ec554fec5d2 \ - --hash=sha256:40b05ab32a5067525670880eb5d169529089a26fe35dce8891127aeddc1950e8 \ - --hash=sha256:48abd62ca76a2cbe034542ed1b6aee851b6f28aaca4e6551b5599b6f3ef175cc \ - --hash=sha256:50cf2a96da226dcbff8671233ecf37bf6e95de98b2a2ebadbfdf455e6d05df42 \ - --hash=sha256:51c4f54dd8c6dfeb58d1df5e4f7f97df8abf17a36626a217f169893d1d7f3e9f \ - --hash=sha256:5bcc90b34df28a4b38653c36bb5ada35671ad105c99cfe915fb5bed7ad6924aa \ - --hash=sha256:62f6d4a29fea082ac4a3c9be5e415218255cf11684ac6ef5488eea0c9132689b \ - --hash=sha256:6eceb6ad197656a1ff49ebfbbfa870678c75be4344feb35ac1edf694309413dc \ - --hash=sha256:7aec8e88a3583515f9e0957fe4f5f6d8d4997e36d0f61624e70469771584c760 \ - --hash=sha256:91ca8145b060679ec9176e6de4f89b07363d6805bd4760631ef254905503598d \ - --hash=sha256:a184243544811e4a50d345838a883733461e67578959ac59964e43cca2c791e7 \ - --hash=sha256:a9e4b426c3702f3cd23b933436487eb34e01e00327fac20c9aebb68ccf34117d \ - --hash=sha256:bb0966e1c50d0ef5bc743312cc730b533491d60585a9a08f897274e57c3f70e0 \ - --hash=sha256:bb8b3c75bd157010459b15222c3fd30577042a7060e29d42dabce449c087f2b3 \ - --hash=sha256:bd5e7d2445d1a958c266bfa5d04c39932dc54093fa391736dbfdb0f1929c1fb3 \ - --hash=sha256:c87d98c7c4a69066fd31701c4e10d178a648c2cac3452e62c6b24dc51f9fcc00 \ - --hash=sha256:d2952396dc604544ea7476b33fe87faedc24d666fb0c2d5ac971a2b9576ab871 \ - --hash=sha256:d8797406499f28b5ef791f339594b0b5fdedf54e203b5066675c406ba69d705c \ - --hash=sha256:d9e9913f7bd69e093b81da4535ce27af842e7bf371cde42d1ae9e9bd382dc0e9 \ - --hash=sha256:e2806553238cd076f0a55bddab37a532b53580e699ed8e5606d0de1f856b5205 \ - --hash=sha256:ebab974b1687509e5c973b5c4b8b146683e101e102e17a86bd196ecaa4d099fc \ - --hash=sha256:ed767bf4ba90104c1216b68111613f0d5926fb3780660ea1198fc469af410e9d \ - --hash=sha256:f7a1fc29803712f80879b0806cb83ab24ce62fc8daf0569f2204a0cfd7f68ed4 - # via - # google-cloud-storage - # google-resumable-media -google-resumable-media==2.7.2 \ - --hash=sha256:3ce7551e9fe6d99e9a126101d2536612bb73486721951e9562fee0f90c6ababa \ - --hash=sha256:5280aed4629f2b60b847b0d42f9857fd4935c11af266744df33d8074cae92fe0 - # via google-cloud-storage -googleapis-common-protos==1.65.0 \ - --hash=sha256:2972e6c496f435b92590fd54045060867f3fe9be2c82ab148fc8885035479a63 \ - --hash=sha256:334a29d07cddc3aa01dee4988f9afd9b2916ee2ff49d6b757155dc0d197852c0 - # via google-api-core -idna==3.10 \ - --hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \ - --hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 - # via requests -importlib-metadata==8.5.0 \ - --hash=sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b \ - --hash=sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7 - # via - # -r requirements.in - # keyring - # twine -jaraco-classes==3.4.0 \ - --hash=sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd \ - --hash=sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790 - # via keyring -jaraco-context==6.0.1 \ - --hash=sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3 \ - --hash=sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4 - # via keyring -jaraco-functools==4.1.0 \ - --hash=sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d \ - --hash=sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649 - # via keyring -jeepney==0.8.0 \ - --hash=sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806 \ - --hash=sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755 - # via - # keyring - # secretstorage -jinja2==3.1.4 \ - --hash=sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369 \ - --hash=sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d - # via gcp-releasetool -keyring==25.4.1 \ - --hash=sha256:5426f817cf7f6f007ba5ec722b1bcad95a75b27d780343772ad76b17cb47b0bf \ - --hash=sha256:b07ebc55f3e8ed86ac81dd31ef14e81ace9dd9c3d4b5d77a6e9a2016d0d71a1b - # via - # gcp-releasetool - # twine -markdown-it-py==3.0.0 \ - --hash=sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 \ - --hash=sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb - # via rich -markupsafe==3.0.1 \ - --hash=sha256:0778de17cff1acaeccc3ff30cd99a3fd5c50fc58ad3d6c0e0c4c58092b859396 \ - --hash=sha256:0f84af7e813784feb4d5e4ff7db633aba6c8ca64a833f61d8e4eade234ef0c38 \ - --hash=sha256:17b2aea42a7280db02ac644db1d634ad47dcc96faf38ab304fe26ba2680d359a \ - --hash=sha256:242d6860f1fd9191aef5fae22b51c5c19767f93fb9ead4d21924e0bcb17619d8 \ - --hash=sha256:244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b \ - --hash=sha256:26627785a54a947f6d7336ce5963569b5d75614619e75193bdb4e06e21d447ad \ - --hash=sha256:2a4b34a8d14649315c4bc26bbfa352663eb51d146e35eef231dd739d54a5430a \ - --hash=sha256:2ae99f31f47d849758a687102afdd05bd3d3ff7dbab0a8f1587981b58a76152a \ - --hash=sha256:312387403cd40699ab91d50735ea7a507b788091c416dd007eac54434aee51da \ - --hash=sha256:3341c043c37d78cc5ae6e3e305e988532b072329639007fd408a476642a89fd6 \ - --hash=sha256:33d1c36b90e570ba7785dacd1faaf091203d9942bc036118fab8110a401eb1a8 \ - --hash=sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344 \ - --hash=sha256:3ffb4a8e7d46ed96ae48805746755fadd0909fea2306f93d5d8233ba23dda12a \ - --hash=sha256:40621d60d0e58aa573b68ac5e2d6b20d44392878e0bfc159012a5787c4e35bc8 \ - --hash=sha256:40f1e10d51c92859765522cbd79c5c8989f40f0419614bcdc5015e7b6bf97fc5 \ - --hash=sha256:45d42d132cff577c92bfba536aefcfea7e26efb975bd455db4e6602f5c9f45e7 \ - --hash=sha256:48488d999ed50ba8d38c581d67e496f955821dc183883550a6fbc7f1aefdc170 \ - --hash=sha256:4935dd7883f1d50e2ffecca0aa33dc1946a94c8f3fdafb8df5c330e48f71b132 \ - --hash=sha256:4c2d64fdba74ad16138300815cfdc6ab2f4647e23ced81f59e940d7d4a1469d9 \ - --hash=sha256:4c8817557d0de9349109acb38b9dd570b03cc5014e8aabf1cbddc6e81005becd \ - --hash=sha256:4ffaaac913c3f7345579db4f33b0020db693f302ca5137f106060316761beea9 \ - --hash=sha256:5a4cb365cb49b750bdb60b846b0c0bc49ed62e59a76635095a179d440540c346 \ - --hash=sha256:62fada2c942702ef8952754abfc1a9f7658a4d5460fabe95ac7ec2cbe0d02abc \ - --hash=sha256:67c519635a4f64e495c50e3107d9b4075aec33634272b5db1cde839e07367589 \ - --hash=sha256:6a54c43d3ec4cf2a39f4387ad044221c66a376e58c0d0e971d47c475ba79c6b5 \ - --hash=sha256:7044312a928a66a4c2a22644147bc61a199c1709712069a344a3fb5cfcf16915 \ - --hash=sha256:730d86af59e0e43ce277bb83970530dd223bf7f2a838e086b50affa6ec5f9295 \ - --hash=sha256:800100d45176652ded796134277ecb13640c1a537cad3b8b53da45aa96330453 \ - --hash=sha256:80fcbf3add8790caddfab6764bde258b5d09aefbe9169c183f88a7410f0f6dea \ - --hash=sha256:82b5dba6eb1bcc29cc305a18a3c5365d2af06ee71b123216416f7e20d2a84e5b \ - --hash=sha256:852dc840f6d7c985603e60b5deaae1d89c56cb038b577f6b5b8c808c97580f1d \ - --hash=sha256:8ad4ad1429cd4f315f32ef263c1342166695fad76c100c5d979c45d5570ed58b \ - --hash=sha256:8ae369e84466aa70f3154ee23c1451fda10a8ee1b63923ce76667e3077f2b0c4 \ - --hash=sha256:93e8248d650e7e9d49e8251f883eed60ecbc0e8ffd6349e18550925e31bd029b \ - --hash=sha256:973a371a55ce9ed333a3a0f8e0bcfae9e0d637711534bcb11e130af2ab9334e7 \ - --hash=sha256:9ba25a71ebf05b9bb0e2ae99f8bc08a07ee8e98c612175087112656ca0f5c8bf \ - --hash=sha256:a10860e00ded1dd0a65b83e717af28845bb7bd16d8ace40fe5531491de76b79f \ - --hash=sha256:a4792d3b3a6dfafefdf8e937f14906a51bd27025a36f4b188728a73382231d91 \ - --hash=sha256:a7420ceda262dbb4b8d839a4ec63d61c261e4e77677ed7c66c99f4e7cb5030dd \ - --hash=sha256:ad91738f14eb8da0ff82f2acd0098b6257621410dcbd4df20aaa5b4233d75a50 \ - --hash=sha256:b6a387d61fe41cdf7ea95b38e9af11cfb1a63499af2759444b99185c4ab33f5b \ - --hash=sha256:b954093679d5750495725ea6f88409946d69cfb25ea7b4c846eef5044194f583 \ - --hash=sha256:bbde71a705f8e9e4c3e9e33db69341d040c827c7afa6789b14c6e16776074f5a \ - --hash=sha256:beeebf760a9c1f4c07ef6a53465e8cfa776ea6a2021eda0d0417ec41043fe984 \ - --hash=sha256:c91b394f7601438ff79a4b93d16be92f216adb57d813a78be4446fe0f6bc2d8c \ - --hash=sha256:c97ff7fedf56d86bae92fa0a646ce1a0ec7509a7578e1ed238731ba13aabcd1c \ - --hash=sha256:cb53e2a99df28eee3b5f4fea166020d3ef9116fdc5764bc5117486e6d1211b25 \ - --hash=sha256:cbf445eb5628981a80f54087f9acdbf84f9b7d862756110d172993b9a5ae81aa \ - --hash=sha256:d06b24c686a34c86c8c1fba923181eae6b10565e4d80bdd7bc1c8e2f11247aa4 \ - --hash=sha256:d98e66a24497637dd31ccab090b34392dddb1f2f811c4b4cd80c230205c074a3 \ - --hash=sha256:db15ce28e1e127a0013dfb8ac243a8e392db8c61eae113337536edb28bdc1f97 \ - --hash=sha256:db842712984e91707437461930e6011e60b39136c7331e971952bb30465bc1a1 \ - --hash=sha256:e24bfe89c6ac4c31792793ad9f861b8f6dc4546ac6dc8f1c9083c7c4f2b335cd \ - --hash=sha256:e81c52638315ff4ac1b533d427f50bc0afc746deb949210bc85f05d4f15fd772 \ - --hash=sha256:e9393357f19954248b00bed7c56f29a25c930593a77630c719653d51e7669c2a \ - --hash=sha256:ee3941769bd2522fe39222206f6dd97ae83c442a94c90f2b7a25d847d40f4729 \ - --hash=sha256:f31ae06f1328595d762c9a2bf29dafd8621c7d3adc130cbb46278079758779ca \ - --hash=sha256:f94190df587738280d544971500b9cafc9b950d32efcb1fba9ac10d84e6aa4e6 \ - --hash=sha256:fa7d686ed9883f3d664d39d5a8e74d3c5f63e603c2e3ff0abcba23eac6542635 \ - --hash=sha256:fb532dd9900381d2e8f48172ddc5a59db4c445a11b9fab40b3b786da40d3b56b \ - --hash=sha256:fe32482b37b4b00c7a52a07211b479653b7fe4f22b2e481b9a9b099d8a430f2f - # via jinja2 -mdurl==0.1.2 \ - --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ - --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba - # via markdown-it-py -more-itertools==10.5.0 \ - --hash=sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef \ - --hash=sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6 - # via - # jaraco-classes - # jaraco-functools -nh3==0.2.18 \ - --hash=sha256:0411beb0589eacb6734f28d5497ca2ed379eafab8ad8c84b31bb5c34072b7164 \ - --hash=sha256:14c5a72e9fe82aea5fe3072116ad4661af5cf8e8ff8fc5ad3450f123e4925e86 \ - --hash=sha256:19aaba96e0f795bd0a6c56291495ff59364f4300d4a39b29a0abc9cb3774a84b \ - --hash=sha256:34c03fa78e328c691f982b7c03d4423bdfd7da69cd707fe572f544cf74ac23ad \ - --hash=sha256:36c95d4b70530b320b365659bb5034341316e6a9b30f0b25fa9c9eff4c27a204 \ - --hash=sha256:3a157ab149e591bb638a55c8c6bcb8cdb559c8b12c13a8affaba6cedfe51713a \ - --hash=sha256:42c64511469005058cd17cc1537578eac40ae9f7200bedcfd1fc1a05f4f8c200 \ - --hash=sha256:5f36b271dae35c465ef5e9090e1fdaba4a60a56f0bb0ba03e0932a66f28b9189 \ - --hash=sha256:6955369e4d9f48f41e3f238a9e60f9410645db7e07435e62c6a9ea6135a4907f \ - --hash=sha256:7b7c2a3c9eb1a827d42539aa64091640bd275b81e097cd1d8d82ef91ffa2e811 \ - --hash=sha256:8ce0f819d2f1933953fca255db2471ad58184a60508f03e6285e5114b6254844 \ - --hash=sha256:94a166927e53972a9698af9542ace4e38b9de50c34352b962f4d9a7d4c927af4 \ - --hash=sha256:a7f1b5b2c15866f2db413a3649a8fe4fd7b428ae58be2c0f6bca5eefd53ca2be \ - --hash=sha256:c8b3a1cebcba9b3669ed1a84cc65bf005728d2f0bc1ed2a6594a992e817f3a50 \ - --hash=sha256:de3ceed6e661954871d6cd78b410213bdcb136f79aafe22aa7182e028b8c7307 \ - --hash=sha256:f0eca9ca8628dbb4e916ae2491d72957fdd35f7a5d326b7032a345f111ac07fe - # via readme-renderer -nox==2024.10.9 \ - --hash=sha256:1d36f309a0a2a853e9bccb76bbef6bb118ba92fa92674d15604ca99adeb29eab \ - --hash=sha256:7aa9dc8d1c27e9f45ab046ffd1c3b2c4f7c91755304769df231308849ebded95 - # via -r requirements.in -packaging==24.1 \ - --hash=sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002 \ - --hash=sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 - # via - # gcp-releasetool - # nox -pkginfo==1.10.0 \ - --hash=sha256:5df73835398d10db79f8eecd5cd86b1f6d29317589ea70796994d49399af6297 \ - --hash=sha256:889a6da2ed7ffc58ab5b900d888ddce90bce912f2d2de1dc1c26f4cb9fe65097 - # via twine -platformdirs==4.3.6 \ - --hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \ - --hash=sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb - # via virtualenv -proto-plus==1.24.0 \ - --hash=sha256:30b72a5ecafe4406b0d339db35b56c4059064e69227b8c3bda7462397f966445 \ - --hash=sha256:402576830425e5f6ce4c2a6702400ac79897dab0b4343821aa5188b0fab81a12 - # via google-api-core -protobuf==5.28.2 \ - --hash=sha256:2c69461a7fcc8e24be697624c09a839976d82ae75062b11a0972e41fd2cd9132 \ - --hash=sha256:35cfcb15f213449af7ff6198d6eb5f739c37d7e4f1c09b5d0641babf2cc0c68f \ - --hash=sha256:52235802093bd8a2811abbe8bf0ab9c5f54cca0a751fdd3f6ac2a21438bffece \ - --hash=sha256:59379674ff119717404f7454647913787034f03fe7049cbef1d74a97bb4593f0 \ - --hash=sha256:5e8a95246d581eef20471b5d5ba010d55f66740942b95ba9b872d918c459452f \ - --hash=sha256:87317e9bcda04a32f2ee82089a204d3a2f0d3c8aeed16568c7daf4756e4f1fe0 \ - --hash=sha256:8ddc60bf374785fb7cb12510b267f59067fa10087325b8e1855b898a0d81d276 \ - --hash=sha256:a8b9403fc70764b08d2f593ce44f1d2920c5077bf7d311fefec999f8c40f78b7 \ - --hash=sha256:c0ea0123dac3399a2eeb1a1443d82b7afc9ff40241433296769f7da42d142ec3 \ - --hash=sha256:ca53faf29896c526863366a52a8f4d88e69cd04ec9571ed6082fa117fac3ab36 \ - --hash=sha256:eeea10f3dc0ac7e6b4933d32db20662902b4ab81bf28df12218aa389e9c2102d - # via - # gcp-docuploader - # gcp-releasetool - # google-api-core - # googleapis-common-protos - # proto-plus -pyasn1==0.6.1 \ - --hash=sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629 \ - --hash=sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034 - # via - # pyasn1-modules - # rsa -pyasn1-modules==0.4.1 \ - --hash=sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd \ - --hash=sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c - # via google-auth -pycparser==2.22 \ - --hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \ - --hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc - # via cffi -pygments==2.18.0 \ - --hash=sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199 \ - --hash=sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a - # via - # readme-renderer - # rich -pyjwt==2.9.0 \ - --hash=sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850 \ - --hash=sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c - # via gcp-releasetool -pyperclip==1.9.0 \ - --hash=sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310 - # via gcp-releasetool -python-dateutil==2.9.0.post0 \ - --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ - --hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 - # via gcp-releasetool -readme-renderer==44.0 \ - --hash=sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151 \ - --hash=sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1 - # via twine -requests==2.32.3 \ - --hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \ - --hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6 - # via - # gcp-releasetool - # google-api-core - # google-cloud-storage - # requests-toolbelt - # twine -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==13.9.2 \ - --hash=sha256:51a2c62057461aaf7152b4d611168f93a9fc73068f8ded2790f29fe2b5366d0c \ - --hash=sha256:8c82a3d3f8dcfe9e734771313e606b39d8247bb6b826e196f4914b333b743cf1 - # via twine -rsa==4.9 \ - --hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \ - --hash=sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21 - # via google-auth -secretstorage==3.3.3 \ - --hash=sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77 \ - --hash=sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99 - # via keyring -six==1.16.0 \ - --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ - --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 - # via - # gcp-docuploader - # python-dateutil -tomli==2.0.2 \ - --hash=sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38 \ - --hash=sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed - # via nox -twine==5.1.1 \ - --hash=sha256:215dbe7b4b94c2c50a7315c0275d2258399280fbb7d04182c7e55e24b5f93997 \ - --hash=sha256:9aa0825139c02b3434d913545c7b847a21c835e11597f5255842d457da2322db - # via -r requirements.in -typing-extensions==4.12.2 \ - --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ - --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8 - # via - # -r requirements.in - # rich -urllib3==2.2.3 \ - --hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac \ - --hash=sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9 - # via - # requests - # twine -virtualenv==20.26.6 \ - --hash=sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48 \ - --hash=sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2 - # via nox -wheel==0.44.0 \ - --hash=sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f \ - --hash=sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49 - # via -r requirements.in -zipp==3.20.2 \ - --hash=sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350 \ - --hash=sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29 - # via importlib-metadata - -# The following packages are considered to be unsafe in a requirements file: -setuptools==75.1.0 \ - --hash=sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2 \ - --hash=sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538 - # via -r requirements.in From fa39b0e87695da40036c1daec1b3108374672d61 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Tue, 11 Mar 2025 03:33:16 +0500 Subject: [PATCH 139/190] fix: allow logs to propagate upstream for caplog testing (#1374) Co-authored-by: Owl Bot --- tests/unit/pubsub_v1/conftest.py | 17 +++++++ .../pubsub_v1/subscriber/test_heartbeater.py | 11 +++-- .../unit/pubsub_v1/subscriber/test_leaser.py | 10 ++-- .../subscriber/test_messages_on_hold.py | 6 +-- .../subscriber/test_streaming_pull_manager.py | 49 +++++++++++++------ 5 files changed, 67 insertions(+), 26 deletions(-) diff --git a/tests/unit/pubsub_v1/conftest.py b/tests/unit/pubsub_v1/conftest.py index b44e2fd84..ab73ab26c 100644 --- a/tests/unit/pubsub_v1/conftest.py +++ b/tests/unit/pubsub_v1/conftest.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging import pytest from opentelemetry.sdk.trace import TracerProvider @@ -43,3 +44,19 @@ def span_exporter(): provider = trace.get_tracer_provider() provider.add_span_processor(processor) yield exporter + + +@pytest.fixture() +def modify_google_logger_propagation(): + """ + Allow propagation of logs to the root logger for tests + that depend on the caplog fixture. Restore the default + propagation setting after the test finishes. + """ + logger = logging.getLogger("google") + original_propagate = logger.propagate + logger.propagate = True + try: + yield + finally: + logger.propagate = original_propagate diff --git a/tests/unit/pubsub_v1/subscriber/test_heartbeater.py b/tests/unit/pubsub_v1/subscriber/test_heartbeater.py index 503fde2c9..857152ac3 100644 --- a/tests/unit/pubsub_v1/subscriber/test_heartbeater.py +++ b/tests/unit/pubsub_v1/subscriber/test_heartbeater.py @@ -28,7 +28,9 @@ import pytest -def test_heartbeat_inactive_manager_active_rpc(caplog): +def test_heartbeat_inactive_manager_active_rpc( + caplog, modify_google_logger_propagation +): caplog.set_level(logging.DEBUG) manager = mock.create_autospec( @@ -46,7 +48,10 @@ def test_heartbeat_inactive_manager_active_rpc(caplog): assert "exiting" in caplog.text -def test_heartbeat_inactive_manager_inactive_rpc(caplog): +def test_heartbeat_inactive_manager_inactive_rpc( + caplog, + modify_google_logger_propagation, +): caplog.set_level(logging.DEBUG) manager = mock.create_autospec( @@ -64,7 +69,7 @@ def test_heartbeat_inactive_manager_inactive_rpc(caplog): assert "exiting" in caplog.text -def test_heartbeat_stopped(caplog): +def test_heartbeat_stopped(caplog, modify_google_logger_propagation): caplog.set_level(logging.DEBUG) manager = mock.create_autospec( streaming_pull_manager.StreamingPullManager, instance=True diff --git a/tests/unit/pubsub_v1/subscriber/test_leaser.py b/tests/unit/pubsub_v1/subscriber/test_leaser.py index b5b5cac20..606dcc2c9 100644 --- a/tests/unit/pubsub_v1/subscriber/test_leaser.py +++ b/tests/unit/pubsub_v1/subscriber/test_leaser.py @@ -53,7 +53,7 @@ def test_add_and_remove(): assert leaser_.bytes == 25 -def test_add_already_managed(caplog): +def test_add_already_managed(caplog, modify_google_logger_propagation): caplog.set_level(logging.DEBUG) leaser_ = leaser.Leaser(mock.sentinel.manager) @@ -64,7 +64,7 @@ def test_add_already_managed(caplog): assert "already lease managed" in caplog.text -def test_remove_not_managed(caplog): +def test_remove_not_managed(caplog, modify_google_logger_propagation): caplog.set_level(logging.DEBUG) leaser_ = leaser.Leaser(mock.sentinel.manager) @@ -74,7 +74,7 @@ def test_remove_not_managed(caplog): assert "not managed" in caplog.text -def test_remove_negative_bytes(caplog): +def test_remove_negative_bytes(caplog, modify_google_logger_propagation): caplog.set_level(logging.DEBUG) leaser_ = leaser.Leaser(mock.sentinel.manager) @@ -98,7 +98,7 @@ def create_manager(flow_control=types.FlowControl()): return manager -def test_maintain_leases_inactive_manager(caplog): +def test_maintain_leases_inactive_manager(caplog, modify_google_logger_propagation): caplog.set_level(logging.DEBUG) manager = create_manager() manager.is_active = False @@ -117,7 +117,7 @@ def test_maintain_leases_inactive_manager(caplog): assert "exiting" in caplog.text -def test_maintain_leases_stopped(caplog): +def test_maintain_leases_stopped(caplog, modify_google_logger_propagation): caplog.set_level(logging.DEBUG) manager = create_manager() diff --git a/tests/unit/pubsub_v1/subscriber/test_messages_on_hold.py b/tests/unit/pubsub_v1/subscriber/test_messages_on_hold.py index 64963de48..0f060e4ea 100644 --- a/tests/unit/pubsub_v1/subscriber/test_messages_on_hold.py +++ b/tests/unit/pubsub_v1/subscriber/test_messages_on_hold.py @@ -140,7 +140,7 @@ def test_ordered_messages_one_key(): assert moh.size == 0 -def test_ordered_messages_drop_duplicate_keys(caplog): +def test_ordered_messages_drop_duplicate_keys(caplog, modify_google_logger_propagation): moh = messages_on_hold.MessagesOnHold() msg1 = make_message(ack_id="ack1", ordering_key="key1") @@ -377,7 +377,7 @@ def test_ordered_and_unordered_messages_interleaved(): assert moh.size == 0 -def test_cleanup_nonexistent_key(caplog): +def test_cleanup_nonexistent_key(caplog, modify_google_logger_propagation): moh = messages_on_hold.MessagesOnHold() moh._clean_up_ordering_key("non-existent-key") assert ( @@ -386,7 +386,7 @@ def test_cleanup_nonexistent_key(caplog): ) -def test_cleanup_key_with_messages(caplog): +def test_cleanup_key_with_messages(caplog, modify_google_logger_propagation): moh = messages_on_hold.MessagesOnHold() # Put message with "key1". diff --git a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py index 4d2d1b98e..f4ceedaf0 100644 --- a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py +++ b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py @@ -603,7 +603,9 @@ def test__maybe_release_messages_below_overload(): assert call_args[1].ack_id in ("ack_foo", "ack_bar") -def test__maybe_release_messages_negative_on_hold_bytes_warning(caplog): +def test__maybe_release_messages_negative_on_hold_bytes_warning( + caplog, modify_google_logger_propagation +): manager = make_manager( flow_control=types.FlowControl(max_messages=10, max_bytes=1000) ) @@ -924,7 +926,7 @@ def test_send_unary_modack_exactly_once_disabled_with_futures(): assert future3.result() == subscriber_exceptions.AcknowledgeStatus.SUCCESS -def test_send_unary_ack_api_call_error(caplog): +def test_send_unary_ack_api_call_error(caplog, modify_google_logger_propagation): caplog.set_level(logging.DEBUG) manager = make_manager() @@ -945,7 +947,7 @@ def test_send_unary_ack_api_call_error(caplog): assert "The front fell off" in caplog.text -def test_send_unary_modack_api_call_error(caplog): +def test_send_unary_modack_api_call_error(caplog, modify_google_logger_propagation): caplog.set_level(logging.DEBUG) manager = make_manager() @@ -978,7 +980,9 @@ def test_send_unary_modack_api_call_error(caplog): assert "The front fell off" in caplog.text -def test_send_unary_ack_retry_error_exactly_once_disabled_no_futures(caplog): +def test_send_unary_ack_retry_error_exactly_once_disabled_no_futures( + caplog, modify_google_logger_propagation +): caplog.set_level(logging.DEBUG) manager, _, _, _, _, _ = make_running_manager() @@ -1014,7 +1018,9 @@ def test_send_unary_ack_retry_error_exactly_once_disabled_no_futures(caplog): assert "signaled streaming pull manager shutdown" in caplog.text -def test_send_unary_ack_retry_error_exactly_once_disabled_with_futures(caplog): +def test_send_unary_ack_retry_error_exactly_once_disabled_with_futures( + caplog, modify_google_logger_propagation +): caplog.set_level(logging.DEBUG) manager, _, _, _, _, _ = make_running_manager() @@ -1054,7 +1060,9 @@ def test_send_unary_ack_retry_error_exactly_once_disabled_with_futures(caplog): assert future2.result() == subscriber_exceptions.AcknowledgeStatus.SUCCESS -def test_send_unary_ack_retry_error_exactly_once_enabled_no_futures(caplog): +def test_send_unary_ack_retry_error_exactly_once_enabled_no_futures( + caplog, modify_google_logger_propagation +): caplog.set_level(logging.DEBUG) manager, _, _, _, _, _ = make_running_manager() @@ -1090,7 +1098,9 @@ def test_send_unary_ack_retry_error_exactly_once_enabled_no_futures(caplog): assert "signaled streaming pull manager shutdown" in caplog.text -def test_send_unary_ack_retry_error_exactly_once_enabled_with_futures(caplog): +def test_send_unary_ack_retry_error_exactly_once_enabled_with_futures( + caplog, modify_google_logger_propagation +): caplog.set_level(logging.DEBUG) manager, _, _, _, _, _ = make_running_manager() @@ -1136,7 +1146,9 @@ def test_send_unary_ack_retry_error_exactly_once_enabled_with_futures(caplog): ) -def test_send_unary_modack_retry_error_exactly_once_disabled_no_future(caplog): +def test_send_unary_modack_retry_error_exactly_once_disabled_no_future( + caplog, modify_google_logger_propagation +): caplog.set_level(logging.DEBUG) manager, _, _, _, _, _ = make_running_manager() @@ -1162,7 +1174,7 @@ def test_send_unary_modack_retry_error_exactly_once_disabled_no_future(caplog): def test_send_unary_modack_retry_error_exactly_once_disabled_with_futures( - caplog, + caplog, modify_google_logger_propagation ): caplog.set_level(logging.DEBUG) @@ -1191,7 +1203,7 @@ def test_send_unary_modack_retry_error_exactly_once_disabled_with_futures( def test_send_unary_modack_retry_error_exactly_once_enabled_no_futures( - caplog, + caplog, modify_google_logger_propagation ): caplog.set_level(logging.DEBUG) @@ -1218,7 +1230,7 @@ def test_send_unary_modack_retry_error_exactly_once_enabled_no_futures( def test_send_unary_modack_retry_error_exactly_once_enabled_with_futures( - caplog, + caplog, modify_google_logger_propagation ): caplog.set_level(logging.DEBUG) @@ -1271,7 +1283,9 @@ def test_heartbeat_inactive(): assert not result -def test_heartbeat_stream_ack_deadline_seconds(caplog): +def test_heartbeat_stream_ack_deadline_seconds( + caplog, modify_google_logger_propagation +): caplog.set_level(logging.DEBUG) manager = make_manager() manager._rpc = mock.create_autospec(bidi.BidiRpc, instance=True) @@ -1922,7 +1936,7 @@ def test__on_response_with_leaser_overload(): assert msg.message_id in ("2", "3") -def test__on_response_none_data(caplog): +def test__on_response_none_data(caplog, modify_google_logger_propagation): caplog.set_level(logging.DEBUG) manager, _, dispatcher, leaser, _, scheduler = make_running_manager() @@ -2087,7 +2101,10 @@ def test__on_response_disable_exactly_once(): assert manager._stream_ack_deadline == 60 -def test__on_response_exactly_once_immediate_modacks_fail(caplog): +def test__on_response_exactly_once_immediate_modacks_fail( + caplog, + modify_google_logger_propagation, +): manager, _, dispatcher, leaser, _, scheduler = make_running_manager() manager._callback = mock.sentinel.callback @@ -2159,7 +2176,9 @@ def complete_futures_with_error(*args, **kwargs): assert manager.load == 0.001 -def test__on_response_exactly_once_immediate_modacks_fail_non_invalid(caplog): +def test__on_response_exactly_once_immediate_modacks_fail_non_invalid( + caplog, modify_google_logger_propagation +): manager, _, dispatcher, leaser, _, scheduler = make_running_manager() manager._callback = mock.sentinel.callback From 43631790781ccfe071a7ecad41949399d3dbd063 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 12:00:31 -0400 Subject: [PATCH 140/190] feat: deprecate `enabled` field for message transforms and add `disabled` field (#1355) Co-authored-by: Owl Bot Co-authored-by: ohmayr Co-authored-by: Anthonios Partheniou --- .../services/publisher/async_client.py | 191 ++- google/pubsub_v1/services/publisher/client.py | 286 ++-- google/pubsub_v1/services/publisher/pagers.py | 48 +- .../services/publisher/transports/grpc.py | 118 +- .../publisher/transports/grpc_asyncio.py | 267 ++-- .../services/publisher/transports/rest.py | 1009 +++++++++++- .../publisher/transports/rest_base.py | 2 - .../services/schema_service/async_client.py | 204 ++- .../services/schema_service/client.py | 299 ++-- .../services/schema_service/pagers.py | 32 +- .../schema_service/transports/grpc.py | 120 +- .../schema_service/transports/grpc_asyncio.py | 269 ++-- .../schema_service/transports/rest.py | 1096 +++++++++++-- .../schema_service/transports/rest_base.py | 2 - .../services/subscriber/async_client.py | 277 +++- .../pubsub_v1/services/subscriber/client.py | 372 +++-- .../pubsub_v1/services/subscriber/pagers.py | 32 +- .../services/subscriber/transports/grpc.py | 132 +- .../subscriber/transports/grpc_asyncio.py | 281 ++-- .../services/subscriber/transports/rest.py | 1361 +++++++++++++++-- .../subscriber/transports/rest_base.py | 2 - google/pubsub_v1/types/pubsub.py | 64 +- .../snippet_metadata_google.pubsub.v1.json | 142 +- scripts/fixup_pubsub_v1_keywords.py | 3 - tests/unit/gapic/pubsub_v1/test_publisher.py | 145 ++ .../gapic/pubsub_v1/test_schema_service.py | 153 ++ tests/unit/gapic/pubsub_v1/test_subscriber.py | 181 +++ 27 files changed, 5835 insertions(+), 1253 deletions(-) diff --git a/google/pubsub_v1/services/publisher/async_client.py b/google/pubsub_v1/services/publisher/async_client.py index 4fd755d91..106ce5f93 100644 --- a/google/pubsub_v1/services/publisher/async_client.py +++ b/google/pubsub_v1/services/publisher/async_client.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import logging as std_logging from collections import OrderedDict import re from typing import ( @@ -54,6 +55,15 @@ from .transports.grpc_asyncio import PublisherGrpcAsyncIOTransport from .client import PublisherClient +try: + from google.api_core import client_logging # type: ignore + + CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER +except ImportError: # pragma: NO COVER + CLIENT_LOGGING_SUPPORTED = False + +_LOGGER = std_logging.getLogger(__name__) + class PublisherAsyncClient: """The service that an application uses to manipulate topics, @@ -259,6 +269,28 @@ def __init__( client_info=client_info, ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + std_logging.DEBUG + ): # pragma: NO COVER + _LOGGER.debug( + "Created client `google.pubsub_v1.PublisherAsyncClient`.", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "universeDomain": getattr( + self._client._transport._credentials, "universe_domain", "" + ), + "credentialsType": f"{type(self._client._transport._credentials).__module__}.{type(self._client._transport._credentials).__qualname__}", + "credentialsInfo": getattr( + self.transport._credentials, "get_cred_info", lambda: None + )(), + } + if hasattr(self._client._transport, "_credentials") + else { + "serviceName": "google.pubsub.v1.Publisher", + "credentialsType": None, + }, + ) + async def create_topic( self, request: Optional[Union[pubsub.Topic, dict]] = None, @@ -266,7 +298,7 @@ async def create_topic( name: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Topic: r"""Creates the given topic with the given name. See the [resource name rules] @@ -318,8 +350,10 @@ async def sample_create_topic(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Topic: @@ -328,7 +362,10 @@ async def sample_create_topic(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name]) + flattened_params = [name] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -379,7 +416,7 @@ async def update_topic( update_mask: Optional[field_mask_pb2.FieldMask] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Topic: r"""Updates an existing topic by updating the fields specified in the update mask. Note that certain @@ -438,8 +475,10 @@ async def sample_update_topic(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Topic: @@ -448,7 +487,10 @@ async def sample_update_topic(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([topic, update_mask]) + flattened_params = [topic, update_mask] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -503,7 +545,7 @@ async def publish( messages: Optional[MutableSequence[pubsub.PubsubMessage]] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.PublishResponse: r"""Adds one or more messages to the topic. Returns ``NOT_FOUND`` if the topic does not exist. @@ -554,8 +596,10 @@ async def sample_publish(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.PublishResponse: @@ -564,7 +608,10 @@ async def sample_publish(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([topic, messages]) + flattened_params = [topic, messages] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -614,7 +661,7 @@ async def get_topic( topic: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Topic: r"""Gets the configuration of a topic. @@ -658,8 +705,10 @@ async def sample_get_topic(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Topic: @@ -668,7 +717,10 @@ async def sample_get_topic(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([topic]) + flattened_params = [topic] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -718,7 +770,7 @@ async def list_topics( project: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pagers.ListTopicsAsyncPager: r"""Lists matching topics. @@ -763,8 +815,10 @@ async def sample_list_topics(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.services.publisher.pagers.ListTopicsAsyncPager: @@ -777,7 +831,10 @@ async def sample_list_topics(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([project]) + flattened_params = [project] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -838,7 +895,7 @@ async def list_topic_subscriptions( topic: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pagers.ListTopicSubscriptionsAsyncPager: r"""Lists the names of the attached subscriptions on this topic. @@ -885,8 +942,10 @@ async def sample_list_topic_subscriptions(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.services.publisher.pagers.ListTopicSubscriptionsAsyncPager: @@ -899,7 +958,10 @@ async def sample_list_topic_subscriptions(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([topic]) + flattened_params = [topic] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -960,7 +1022,7 @@ async def list_topic_snapshots( topic: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pagers.ListTopicSnapshotsAsyncPager: r"""Lists the names of the snapshots on this topic. Snapshots are used in @@ -1011,8 +1073,10 @@ async def sample_list_topic_snapshots(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.services.publisher.pagers.ListTopicSnapshotsAsyncPager: @@ -1025,7 +1089,10 @@ async def sample_list_topic_snapshots(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([topic]) + flattened_params = [topic] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1086,7 +1153,7 @@ async def delete_topic( topic: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> None: r"""Deletes the topic with the given name. Returns ``NOT_FOUND`` if the topic does not exist. After a topic is deleted, a new topic @@ -1132,13 +1199,18 @@ async def sample_delete_topic(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([topic]) + flattened_params = [topic] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1184,7 +1256,7 @@ async def detach_subscription( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.DetachSubscriptionResponse: r"""Detaches a subscription from this topic. All messages retained in the subscription are dropped. Subsequent ``Pull`` and @@ -1226,8 +1298,10 @@ async def sample_detach_subscription(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.DetachSubscriptionResponse: @@ -1275,22 +1349,24 @@ async def set_iam_policy( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Sets the IAM access control policy on the specified function. Replaces any existing policy. Args: - request (:class:`~.policy_pb2.SetIamPolicyRequest`): + request (:class:`~.iam_policy_pb2.SetIamPolicyRequest`): The request object. Request message for `SetIamPolicy` method. retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.policy_pb2.Policy: Defines an Identity and Access Management (IAM) policy. @@ -1310,6 +1386,7 @@ async def set_iam_policy( **JSON Example** :: + { "bindings": [ { @@ -1394,23 +1471,25 @@ async def get_iam_policy( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Gets the IAM access control policy for a function. - Returns an empty policy if the function exists and does - not have a policy set. + Returns an empty policy if the function exists and does not have a + policy set. Args: request (:class:`~.iam_policy_pb2.GetIamPolicyRequest`): The request object. Request message for `GetIamPolicy` method. - retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, - should be retried. + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if + any, should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.policy_pb2.Policy: Defines an Identity and Access Management (IAM) policy. @@ -1515,26 +1594,28 @@ async def test_iam_permissions( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> iam_policy_pb2.TestIamPermissionsResponse: - r"""Tests the specified permissions against the IAM access control + r"""Tests the specified IAM permissions against the IAM access control policy for a function. - If the function does not exist, this will - return an empty set of permissions, not a NOT_FOUND error. + If the function does not exist, this will return an empty set + of permissions, not a NOT_FOUND error. Args: request (:class:`~.iam_policy_pb2.TestIamPermissionsRequest`): The request object. Request message for `TestIamPermissions` method. - retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, - should be retried. + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, + if any, should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: - ~iam_policy_pb2.PolicyTestIamPermissionsResponse: + ~.iam_policy_pb2.TestIamPermissionsResponse: Response message for ``TestIamPermissions`` method. """ # Create or coerce a protobuf request object. diff --git a/google/pubsub_v1/services/publisher/client.py b/google/pubsub_v1/services/publisher/client.py index 698aed49e..2db3e1cda 100644 --- a/google/pubsub_v1/services/publisher/client.py +++ b/google/pubsub_v1/services/publisher/client.py @@ -14,6 +14,9 @@ # limitations under the License. # from collections import OrderedDict +from http import HTTPStatus +import json +import logging as std_logging import functools import os import re @@ -50,6 +53,15 @@ except AttributeError: # pragma: NO COVER OptionalRetry = Union[retries.Retry, object, None] # type: ignore +try: + from google.api_core import client_logging # type: ignore + + CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER +except ImportError: # pragma: NO COVER + CLIENT_LOGGING_SUPPORTED = False + +_LOGGER = std_logging.getLogger(__name__) + from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore from google.protobuf import duration_pb2 # type: ignore @@ -524,6 +536,33 @@ def _validate_universe_domain(self): # NOTE (b/349488459): universe validation is disabled until further notice. return True + def _add_cred_info_for_auth_errors( + self, error: core_exceptions.GoogleAPICallError + ) -> None: + """Adds credential info string to error details for 401/403/404 errors. + + Args: + error (google.api_core.exceptions.GoogleAPICallError): The error to add the cred info. + """ + if error.code not in [ + HTTPStatus.UNAUTHORIZED, + HTTPStatus.FORBIDDEN, + HTTPStatus.NOT_FOUND, + ]: + return + + cred = self._transport._credentials + + # get_cred_info is only available in google-auth>=2.35.0 + if not hasattr(cred, "get_cred_info"): + return + + # ignore the type check since pypy test fails when get_cred_info + # is not available + cred_info = cred.get_cred_info() # type: ignore + if cred_info and hasattr(error._details, "append"): + error._details.append(json.dumps(cred_info)) + @property def api_endpoint(self): """Return the API endpoint used by the client instance. @@ -628,6 +667,10 @@ def __init__( # Initialize the universe domain validation. self._is_universe_domain_valid = False + if CLIENT_LOGGING_SUPPORTED: # pragma: NO COVER + # Setup logging. + client_logging.initialize_logging() + api_key_value = getattr(self._client_options, "api_key", None) if api_key_value and credentials: raise ValueError( @@ -699,6 +742,29 @@ def __init__( api_audience=self._client_options.api_audience, ) + if "async" not in str(self._transport): + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + std_logging.DEBUG + ): # pragma: NO COVER + _LOGGER.debug( + "Created client `google.pubsub_v1.PublisherClient`.", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "universeDomain": getattr( + self._transport._credentials, "universe_domain", "" + ), + "credentialsType": f"{type(self._transport._credentials).__module__}.{type(self._transport._credentials).__qualname__}", + "credentialsInfo": getattr( + self.transport._credentials, "get_cred_info", lambda: None + )(), + } + if hasattr(self._transport, "_credentials") + else { + "serviceName": "google.pubsub.v1.Publisher", + "credentialsType": None, + }, + ) + def create_topic( self, request: Optional[Union[pubsub.Topic, dict]] = None, @@ -706,7 +772,7 @@ def create_topic( name: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Topic: r"""Creates the given topic with the given name. See the [resource name rules] @@ -758,8 +824,10 @@ def sample_create_topic(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Topic: @@ -768,7 +836,10 @@ def sample_create_topic(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name]) + flattened_params = [name] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -816,7 +887,7 @@ def update_topic( update_mask: Optional[field_mask_pb2.FieldMask] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Topic: r"""Updates an existing topic by updating the fields specified in the update mask. Note that certain @@ -875,8 +946,10 @@ def sample_update_topic(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Topic: @@ -885,7 +958,10 @@ def sample_update_topic(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([topic, update_mask]) + flattened_params = [topic, update_mask] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -937,7 +1013,7 @@ def publish( messages: Optional[MutableSequence[pubsub.PubsubMessage]] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.PublishResponse: r"""Adds one or more messages to the topic. Returns ``NOT_FOUND`` if the topic does not exist. @@ -988,8 +1064,10 @@ def sample_publish(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.PublishResponse: @@ -998,7 +1076,10 @@ def sample_publish(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([topic, messages]) + flattened_params = [topic, messages] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1047,7 +1128,7 @@ def get_topic( topic: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Topic: r"""Gets the configuration of a topic. @@ -1091,8 +1172,10 @@ def sample_get_topic(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Topic: @@ -1101,7 +1184,10 @@ def sample_get_topic(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([topic]) + flattened_params = [topic] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1148,7 +1234,7 @@ def list_topics( project: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pagers.ListTopicsPager: r"""Lists matching topics. @@ -1193,8 +1279,10 @@ def sample_list_topics(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.services.publisher.pagers.ListTopicsPager: @@ -1207,7 +1295,10 @@ def sample_list_topics(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([project]) + flattened_params = [project] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1265,7 +1356,7 @@ def list_topic_subscriptions( topic: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pagers.ListTopicSubscriptionsPager: r"""Lists the names of the attached subscriptions on this topic. @@ -1312,8 +1403,10 @@ def sample_list_topic_subscriptions(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.services.publisher.pagers.ListTopicSubscriptionsPager: @@ -1326,7 +1419,10 @@ def sample_list_topic_subscriptions(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([topic]) + flattened_params = [topic] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1384,7 +1480,7 @@ def list_topic_snapshots( topic: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pagers.ListTopicSnapshotsPager: r"""Lists the names of the snapshots on this topic. Snapshots are used in @@ -1435,8 +1531,10 @@ def sample_list_topic_snapshots(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.services.publisher.pagers.ListTopicSnapshotsPager: @@ -1449,7 +1547,10 @@ def sample_list_topic_snapshots(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([topic]) + flattened_params = [topic] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1507,7 +1608,7 @@ def delete_topic( topic: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> None: r"""Deletes the topic with the given name. Returns ``NOT_FOUND`` if the topic does not exist. After a topic is deleted, a new topic @@ -1553,13 +1654,18 @@ def sample_delete_topic(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([topic]) + flattened_params = [topic] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1602,7 +1708,7 @@ def detach_subscription( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.DetachSubscriptionResponse: r"""Detaches a subscription from this topic. All messages retained in the subscription are dropped. Subsequent ``Pull`` and @@ -1644,8 +1750,10 @@ def sample_detach_subscription(): should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.DetachSubscriptionResponse: @@ -1704,7 +1812,7 @@ def set_iam_policy( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Sets the IAM access control policy on the specified function. @@ -1718,8 +1826,10 @@ def set_iam_policy( should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.policy_pb2.Policy: Defines an Identity and Access Management (IAM) policy. @@ -1796,11 +1906,7 @@ def set_iam_policy( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method.wrap_method( - self._transport.set_iam_policy, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._transport._wrapped_methods[self._transport.set_iam_policy] # Certain fields should be provided within the metadata header; # add these here. @@ -1811,16 +1917,20 @@ def set_iam_policy( # Validate the universe domain. self._validate_universe_domain() - # Send the request. - response = rpc( - request, - retry=retry, - timeout=timeout, - metadata=metadata, - ) + try: + # Send the request. + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) - # Done; return the response. - return response + # Done; return the response. + return response + except core_exceptions.GoogleAPICallError as e: + self._add_cred_info_for_auth_errors(e) + raise e def get_iam_policy( self, @@ -1828,7 +1938,7 @@ def get_iam_policy( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Gets the IAM access control policy for a function. @@ -1843,8 +1953,10 @@ def get_iam_policy( any, should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.policy_pb2.Policy: Defines an Identity and Access Management (IAM) policy. @@ -1921,11 +2033,7 @@ def get_iam_policy( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method.wrap_method( - self._transport.get_iam_policy, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._transport._wrapped_methods[self._transport.get_iam_policy] # Certain fields should be provided within the metadata header; # add these here. @@ -1936,16 +2044,20 @@ def get_iam_policy( # Validate the universe domain. self._validate_universe_domain() - # Send the request. - response = rpc( - request, - retry=retry, - timeout=timeout, - metadata=metadata, - ) + try: + # Send the request. + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) - # Done; return the response. - return response + # Done; return the response. + return response + except core_exceptions.GoogleAPICallError as e: + self._add_cred_info_for_auth_errors(e) + raise e def test_iam_permissions( self, @@ -1953,7 +2065,7 @@ def test_iam_permissions( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: TimeoutType = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> iam_policy_pb2.TestIamPermissionsResponse: r"""Tests the specified IAM permissions against the IAM access control policy for a function. @@ -1969,8 +2081,10 @@ def test_iam_permissions( if any, should be retried. timeout (TimeoutType): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.iam_policy_pb2.TestIamPermissionsResponse: Response message for ``TestIamPermissions`` method. @@ -1984,11 +2098,7 @@ def test_iam_permissions( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method.wrap_method( - self._transport.test_iam_permissions, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._transport._wrapped_methods[self._transport.test_iam_permissions] # Certain fields should be provided within the metadata header; # add these here. @@ -1999,16 +2109,20 @@ def test_iam_permissions( # Validate the universe domain. self._validate_universe_domain() - # Send the request. - response = rpc( - request, - retry=retry, - timeout=timeout, - metadata=metadata, - ) + try: + # Send the request. + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) - # Done; return the response. - return response + # Done; return the response. + return response + except core_exceptions.GoogleAPICallError as e: + self._add_cred_info_for_auth_errors(e) + raise e DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( diff --git a/google/pubsub_v1/services/publisher/pagers.py b/google/pubsub_v1/services/publisher/pagers.py index de3490c39..da6de8dfa 100644 --- a/google/pubsub_v1/services/publisher/pagers.py +++ b/google/pubsub_v1/services/publisher/pagers.py @@ -66,7 +66,7 @@ def __init__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = () + metadata: Sequence[Tuple[str, Union[str, bytes]]] = () ): """Instantiate the pager. @@ -80,8 +80,10 @@ def __init__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ self._method = method self._request = pubsub.ListTopicsRequest(request) @@ -140,7 +142,7 @@ def __init__( *, retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = () + metadata: Sequence[Tuple[str, Union[str, bytes]]] = () ): """Instantiates the pager. @@ -154,8 +156,10 @@ def __init__( retry (google.api_core.retry.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ self._method = method self._request = pubsub.ListTopicsRequest(request) @@ -218,7 +222,7 @@ def __init__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = () + metadata: Sequence[Tuple[str, Union[str, bytes]]] = () ): """Instantiate the pager. @@ -232,8 +236,10 @@ def __init__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ self._method = method self._request = pubsub.ListTopicSubscriptionsRequest(request) @@ -292,7 +298,7 @@ def __init__( *, retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = () + metadata: Sequence[Tuple[str, Union[str, bytes]]] = () ): """Instantiates the pager. @@ -306,8 +312,10 @@ def __init__( retry (google.api_core.retry.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ self._method = method self._request = pubsub.ListTopicSubscriptionsRequest(request) @@ -370,7 +378,7 @@ def __init__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = () + metadata: Sequence[Tuple[str, Union[str, bytes]]] = () ): """Instantiate the pager. @@ -384,8 +392,10 @@ def __init__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ self._method = method self._request = pubsub.ListTopicSnapshotsRequest(request) @@ -444,7 +454,7 @@ def __init__( *, retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = () + metadata: Sequence[Tuple[str, Union[str, bytes]]] = () ): """Instantiates the pager. @@ -458,8 +468,10 @@ def __init__( retry (google.api_core.retry.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ self._method = method self._request = pubsub.ListTopicSnapshotsRequest(request) diff --git a/google/pubsub_v1/services/publisher/transports/grpc.py b/google/pubsub_v1/services/publisher/transports/grpc.py index b6e07b21e..3b92a0c31 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc.py +++ b/google/pubsub_v1/services/publisher/transports/grpc.py @@ -13,6 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import json +import logging as std_logging +import pickle import warnings from typing import Callable, Dict, Optional, Sequence, Tuple, Union @@ -21,8 +24,11 @@ import google.auth # type: ignore from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore +from google.protobuf.json_format import MessageToJson +import google.protobuf.message import grpc # type: ignore +import proto # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -30,6 +36,81 @@ from google.pubsub_v1.types import pubsub from .base import PublisherTransport, DEFAULT_CLIENT_INFO +try: + from google.api_core import client_logging # type: ignore + + CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER +except ImportError: # pragma: NO COVER + CLIENT_LOGGING_SUPPORTED = False + +_LOGGER = std_logging.getLogger(__name__) + + +class _LoggingClientInterceptor(grpc.UnaryUnaryClientInterceptor): # pragma: NO COVER + def intercept_unary_unary(self, continuation, client_call_details, request): + logging_enabled = CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + std_logging.DEBUG + ) + if logging_enabled: # pragma: NO COVER + request_metadata = client_call_details.metadata + if isinstance(request, proto.Message): + request_payload = type(request).to_json(request) + elif isinstance(request, google.protobuf.message.Message): + request_payload = MessageToJson(request) + else: + request_payload = f"{type(request).__name__}: {pickle.dumps(request)}" + + request_metadata = { + key: value.decode("utf-8") if isinstance(value, bytes) else value + for key, value in request_metadata + } + grpc_request = { + "payload": request_payload, + "requestMethod": "grpc", + "metadata": dict(request_metadata), + } + _LOGGER.debug( + f"Sending request for {client_call_details.method}", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": client_call_details.method, + "request": grpc_request, + "metadata": grpc_request["metadata"], + }, + ) + + response = continuation(client_call_details, request) + if logging_enabled: # pragma: NO COVER + response_metadata = response.trailing_metadata() + # Convert gRPC metadata `` to list of tuples + metadata = ( + dict([(k, str(v)) for k, v in response_metadata]) + if response_metadata + else None + ) + result = response.result() + if isinstance(result, proto.Message): + response_payload = type(result).to_json(result) + elif isinstance(result, google.protobuf.message.Message): + response_payload = MessageToJson(result) + else: + response_payload = f"{type(result).__name__}: {pickle.dumps(result)}" + grpc_response = { + "payload": response_payload, + "metadata": metadata, + "status": "OK", + } + _LOGGER.debug( + f"Received response for {client_call_details.method}.", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": client_call_details.method, + "response": grpc_response, + "metadata": grpc_response["metadata"], + }, + ) + return response + class PublisherGrpcTransport(PublisherTransport): """gRPC backend transport for Publisher. @@ -186,7 +267,12 @@ def __init__( ], ) - # Wrap messages. This must be done after self._grpc_channel exists + self._interceptor = _LoggingClientInterceptor() + self._logged_channel = grpc.intercept_channel( + self._grpc_channel, self._interceptor + ) + + # Wrap messages. This must be done after self._logged_channel exists self._prep_wrapped_messages(client_info) @classmethod @@ -260,7 +346,7 @@ def create_topic(self) -> Callable[[pubsub.Topic], pubsub.Topic]: # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "create_topic" not in self._stubs: - self._stubs["create_topic"] = self.grpc_channel.unary_unary( + self._stubs["create_topic"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/CreateTopic", request_serializer=pubsub.Topic.serialize, response_deserializer=pubsub.Topic.deserialize, @@ -286,7 +372,7 @@ def update_topic(self) -> Callable[[pubsub.UpdateTopicRequest], pubsub.Topic]: # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "update_topic" not in self._stubs: - self._stubs["update_topic"] = self.grpc_channel.unary_unary( + self._stubs["update_topic"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/UpdateTopic", request_serializer=pubsub.UpdateTopicRequest.serialize, response_deserializer=pubsub.Topic.deserialize, @@ -311,7 +397,7 @@ def publish(self) -> Callable[[pubsub.PublishRequest], pubsub.PublishResponse]: # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "publish" not in self._stubs: - self._stubs["publish"] = self.grpc_channel.unary_unary( + self._stubs["publish"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/Publish", request_serializer=pubsub.PublishRequest.serialize, response_deserializer=pubsub.PublishResponse.deserialize, @@ -335,7 +421,7 @@ def get_topic(self) -> Callable[[pubsub.GetTopicRequest], pubsub.Topic]: # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "get_topic" not in self._stubs: - self._stubs["get_topic"] = self.grpc_channel.unary_unary( + self._stubs["get_topic"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/GetTopic", request_serializer=pubsub.GetTopicRequest.serialize, response_deserializer=pubsub.Topic.deserialize, @@ -361,7 +447,7 @@ def list_topics( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "list_topics" not in self._stubs: - self._stubs["list_topics"] = self.grpc_channel.unary_unary( + self._stubs["list_topics"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/ListTopics", request_serializer=pubsub.ListTopicsRequest.serialize, response_deserializer=pubsub.ListTopicsResponse.deserialize, @@ -390,7 +476,7 @@ def list_topic_subscriptions( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "list_topic_subscriptions" not in self._stubs: - self._stubs["list_topic_subscriptions"] = self.grpc_channel.unary_unary( + self._stubs["list_topic_subscriptions"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/ListTopicSubscriptions", request_serializer=pubsub.ListTopicSubscriptionsRequest.serialize, response_deserializer=pubsub.ListTopicSubscriptionsResponse.deserialize, @@ -423,7 +509,7 @@ def list_topic_snapshots( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "list_topic_snapshots" not in self._stubs: - self._stubs["list_topic_snapshots"] = self.grpc_channel.unary_unary( + self._stubs["list_topic_snapshots"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/ListTopicSnapshots", request_serializer=pubsub.ListTopicSnapshotsRequest.serialize, response_deserializer=pubsub.ListTopicSnapshotsResponse.deserialize, @@ -452,7 +538,7 @@ def delete_topic(self) -> Callable[[pubsub.DeleteTopicRequest], empty_pb2.Empty] # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "delete_topic" not in self._stubs: - self._stubs["delete_topic"] = self.grpc_channel.unary_unary( + self._stubs["delete_topic"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/DeleteTopic", request_serializer=pubsub.DeleteTopicRequest.serialize, response_deserializer=empty_pb2.Empty.FromString, @@ -484,13 +570,16 @@ def detach_subscription( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "detach_subscription" not in self._stubs: - self._stubs["detach_subscription"] = self.grpc_channel.unary_unary( + self._stubs["detach_subscription"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/DetachSubscription", request_serializer=pubsub.DetachSubscriptionRequest.serialize, response_deserializer=pubsub.DetachSubscriptionResponse.deserialize, ) return self._stubs["detach_subscription"] + def close(self): + self._logged_channel.close() + @property def set_iam_policy( self, @@ -509,7 +598,7 @@ def set_iam_policy( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "set_iam_policy" not in self._stubs: - self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( + self._stubs["set_iam_policy"] = self._logged_channel.unary_unary( "/google.iam.v1.IAMPolicy/SetIamPolicy", request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, response_deserializer=policy_pb2.Policy.FromString, @@ -535,7 +624,7 @@ def get_iam_policy( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "get_iam_policy" not in self._stubs: - self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( + self._stubs["get_iam_policy"] = self._logged_channel.unary_unary( "/google.iam.v1.IAMPolicy/GetIamPolicy", request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, response_deserializer=policy_pb2.Policy.FromString, @@ -564,16 +653,13 @@ def test_iam_permissions( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "test_iam_permissions" not in self._stubs: - self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( + self._stubs["test_iam_permissions"] = self._logged_channel.unary_unary( "/google.iam.v1.IAMPolicy/TestIamPermissions", request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, ) return self._stubs["test_iam_permissions"] - def close(self): - self.grpc_channel.close() - @property def kind(self) -> str: return "grpc" diff --git a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py index 6a293137a..2b6e8c603 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py @@ -14,6 +14,9 @@ # limitations under the License. # import inspect +import json +import pickle +import logging as std_logging import warnings from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union @@ -23,8 +26,11 @@ from google.api_core import retry_async as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore +from google.protobuf.json_format import MessageToJson +import google.protobuf.message import grpc # type: ignore +import proto # type: ignore from grpc.experimental import aio # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore @@ -34,6 +40,82 @@ from .base import PublisherTransport, DEFAULT_CLIENT_INFO from .grpc import PublisherGrpcTransport +try: + from google.api_core import client_logging # type: ignore + + CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER +except ImportError: # pragma: NO COVER + CLIENT_LOGGING_SUPPORTED = False + +_LOGGER = std_logging.getLogger(__name__) + + +class _LoggingClientAIOInterceptor( + grpc.aio.UnaryUnaryClientInterceptor +): # pragma: NO COVER + async def intercept_unary_unary(self, continuation, client_call_details, request): + logging_enabled = CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + std_logging.DEBUG + ) + if logging_enabled: # pragma: NO COVER + request_metadata = client_call_details.metadata + if isinstance(request, proto.Message): + request_payload = type(request).to_json(request) + elif isinstance(request, google.protobuf.message.Message): + request_payload = MessageToJson(request) + else: + request_payload = f"{type(request).__name__}: {pickle.dumps(request)}" + + request_metadata = { + key: value.decode("utf-8") if isinstance(value, bytes) else value + for key, value in request_metadata + } + grpc_request = { + "payload": request_payload, + "requestMethod": "grpc", + "metadata": dict(request_metadata), + } + _LOGGER.debug( + f"Sending request for {client_call_details.method}", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": str(client_call_details.method), + "request": grpc_request, + "metadata": grpc_request["metadata"], + }, + ) + response = await continuation(client_call_details, request) + if logging_enabled: # pragma: NO COVER + response_metadata = await response.trailing_metadata() + # Convert gRPC metadata `` to list of tuples + metadata = ( + dict([(k, str(v)) for k, v in response_metadata]) + if response_metadata + else None + ) + result = await response + if isinstance(result, proto.Message): + response_payload = type(result).to_json(result) + elif isinstance(result, google.protobuf.message.Message): + response_payload = MessageToJson(result) + else: + response_payload = f"{type(result).__name__}: {pickle.dumps(result)}" + grpc_response = { + "payload": response_payload, + "metadata": metadata, + "status": "OK", + } + _LOGGER.debug( + f"Received response to rpc {client_call_details.method}.", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": str(client_call_details.method), + "response": grpc_response, + "metadata": grpc_response["metadata"], + }, + ) + return response + class PublisherGrpcAsyncIOTransport(PublisherTransport): """gRPC AsyncIO backend transport for Publisher. @@ -233,10 +315,13 @@ def __init__( ], ) - # Wrap messages. This must be done after self._grpc_channel exists + self._interceptor = _LoggingClientAIOInterceptor() + self._grpc_channel._unary_unary_interceptors.append(self._interceptor) + self._logged_channel = self._grpc_channel self._wrap_with_kind = ( "kind" in inspect.signature(gapic_v1.method_async.wrap_method).parameters ) + # Wrap messages. This must be done after self._logged_channel exists self._prep_wrapped_messages(client_info) @property @@ -268,7 +353,7 @@ def create_topic(self) -> Callable[[pubsub.Topic], Awaitable[pubsub.Topic]]: # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "create_topic" not in self._stubs: - self._stubs["create_topic"] = self.grpc_channel.unary_unary( + self._stubs["create_topic"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/CreateTopic", request_serializer=pubsub.Topic.serialize, response_deserializer=pubsub.Topic.deserialize, @@ -296,7 +381,7 @@ def update_topic( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "update_topic" not in self._stubs: - self._stubs["update_topic"] = self.grpc_channel.unary_unary( + self._stubs["update_topic"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/UpdateTopic", request_serializer=pubsub.UpdateTopicRequest.serialize, response_deserializer=pubsub.Topic.deserialize, @@ -323,7 +408,7 @@ def publish( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "publish" not in self._stubs: - self._stubs["publish"] = self.grpc_channel.unary_unary( + self._stubs["publish"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/Publish", request_serializer=pubsub.PublishRequest.serialize, response_deserializer=pubsub.PublishResponse.deserialize, @@ -347,7 +432,7 @@ def get_topic(self) -> Callable[[pubsub.GetTopicRequest], Awaitable[pubsub.Topic # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "get_topic" not in self._stubs: - self._stubs["get_topic"] = self.grpc_channel.unary_unary( + self._stubs["get_topic"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/GetTopic", request_serializer=pubsub.GetTopicRequest.serialize, response_deserializer=pubsub.Topic.deserialize, @@ -373,7 +458,7 @@ def list_topics( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "list_topics" not in self._stubs: - self._stubs["list_topics"] = self.grpc_channel.unary_unary( + self._stubs["list_topics"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/ListTopics", request_serializer=pubsub.ListTopicsRequest.serialize, response_deserializer=pubsub.ListTopicsResponse.deserialize, @@ -403,7 +488,7 @@ def list_topic_subscriptions( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "list_topic_subscriptions" not in self._stubs: - self._stubs["list_topic_subscriptions"] = self.grpc_channel.unary_unary( + self._stubs["list_topic_subscriptions"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/ListTopicSubscriptions", request_serializer=pubsub.ListTopicSubscriptionsRequest.serialize, response_deserializer=pubsub.ListTopicSubscriptionsResponse.deserialize, @@ -436,7 +521,7 @@ def list_topic_snapshots( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "list_topic_snapshots" not in self._stubs: - self._stubs["list_topic_snapshots"] = self.grpc_channel.unary_unary( + self._stubs["list_topic_snapshots"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/ListTopicSnapshots", request_serializer=pubsub.ListTopicSnapshotsRequest.serialize, response_deserializer=pubsub.ListTopicSnapshotsResponse.deserialize, @@ -467,7 +552,7 @@ def delete_topic( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "delete_topic" not in self._stubs: - self._stubs["delete_topic"] = self.grpc_channel.unary_unary( + self._stubs["delete_topic"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/DeleteTopic", request_serializer=pubsub.DeleteTopicRequest.serialize, response_deserializer=empty_pb2.Empty.FromString, @@ -499,93 +584,13 @@ def detach_subscription( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "detach_subscription" not in self._stubs: - self._stubs["detach_subscription"] = self.grpc_channel.unary_unary( + self._stubs["detach_subscription"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Publisher/DetachSubscription", request_serializer=pubsub.DetachSubscriptionRequest.serialize, response_deserializer=pubsub.DetachSubscriptionResponse.deserialize, ) return self._stubs["detach_subscription"] - @property - def set_iam_policy( - self, - ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: - r"""Return a callable for the set iam policy method over gRPC. - Sets the IAM access control policy on the specified - function. Replaces any existing policy. - Returns: - Callable[[~.SetIamPolicyRequest], - Awaitable[~.Policy]]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "set_iam_policy" not in self._stubs: - self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( - "/google.iam.v1.IAMPolicy/SetIamPolicy", - request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, - response_deserializer=policy_pb2.Policy.FromString, - ) - return self._stubs["set_iam_policy"] - - @property - def get_iam_policy( - self, - ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: - r"""Return a callable for the get iam policy method over gRPC. - Gets the IAM access control policy for a function. - Returns an empty policy if the function exists and does - not have a policy set. - Returns: - Callable[[~.GetIamPolicyRequest], - Awaitable[~.Policy]]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "get_iam_policy" not in self._stubs: - self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( - "/google.iam.v1.IAMPolicy/GetIamPolicy", - request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, - response_deserializer=policy_pb2.Policy.FromString, - ) - return self._stubs["get_iam_policy"] - - @property - def test_iam_permissions( - self, - ) -> Callable[ - [iam_policy_pb2.TestIamPermissionsRequest], - Awaitable[iam_policy_pb2.TestIamPermissionsResponse], - ]: - r"""Return a callable for the test iam permissions method over gRPC. - Tests the specified permissions against the IAM access control - policy for a function. If the function does not exist, this will - return an empty set of permissions, not a NOT_FOUND error. - Returns: - Callable[[~.TestIamPermissionsRequest], - Awaitable[~.TestIamPermissionsResponse]]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "test_iam_permissions" not in self._stubs: - self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( - "/google.iam.v1.IAMPolicy/TestIamPermissions", - request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, - response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, - ) - return self._stubs["test_iam_permissions"] - def _prep_wrapped_messages(self, client_info): """Precompute the wrapped methods, overriding the base class method to use async wrappers.""" self._wrapped_methods = { @@ -752,11 +757,91 @@ def _wrap_method(self, func, *args, **kwargs): return gapic_v1.method_async.wrap_method(func, *args, **kwargs) def close(self): - return self.grpc_channel.close() + return self._logged_channel.close() @property def kind(self) -> str: return "grpc_asyncio" + @property + def set_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the set iam policy method over gRPC. + Sets the IAM access control policy on the specified + function. Replaces any existing policy. + Returns: + Callable[[~.SetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "set_iam_policy" not in self._stubs: + self._stubs["set_iam_policy"] = self._logged_channel.unary_unary( + "/google.iam.v1.IAMPolicy/SetIamPolicy", + request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["set_iam_policy"] + + @property + def get_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the get iam policy method over gRPC. + Gets the IAM access control policy for a function. + Returns an empty policy if the function exists and does + not have a policy set. + Returns: + Callable[[~.GetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_iam_policy" not in self._stubs: + self._stubs["get_iam_policy"] = self._logged_channel.unary_unary( + "/google.iam.v1.IAMPolicy/GetIamPolicy", + request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["get_iam_policy"] + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + iam_policy_pb2.TestIamPermissionsResponse, + ]: + r"""Return a callable for the test iam permissions method over gRPC. + Tests the specified permissions against the IAM access control + policy for a function. If the function does not exist, this will + return an empty set of permissions, not a NOT_FOUND error. + Returns: + Callable[[~.TestIamPermissionsRequest], + ~.TestIamPermissionsResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "test_iam_permissions" not in self._stubs: + self._stubs["test_iam_permissions"] = self._logged_channel.unary_unary( + "/google.iam.v1.IAMPolicy/TestIamPermissions", + request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, + response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, + ) + return self._stubs["test_iam_permissions"] + __all__ = ("PublisherGrpcAsyncIOTransport",) diff --git a/google/pubsub_v1/services/publisher/transports/rest.py b/google/pubsub_v1/services/publisher/transports/rest.py index 3685dd55f..0089968cc 100644 --- a/google/pubsub_v1/services/publisher/transports/rest.py +++ b/google/pubsub_v1/services/publisher/transports/rest.py @@ -13,9 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import logging +import json # type: ignore from google.auth.transport.requests import AuthorizedSession # type: ignore -import json # type: ignore from google.auth import credentials as ga_credentials # type: ignore from google.api_core import exceptions as core_exceptions from google.api_core import retry as retries @@ -33,8 +34,6 @@ import warnings -from google.iam.v1 import iam_policy_pb2 # type: ignore -from google.iam.v1 import policy_pb2 # type: ignore from google.protobuf import empty_pb2 # type: ignore from google.pubsub_v1.types import pubsub @@ -47,6 +46,14 @@ except AttributeError: # pragma: NO COVER OptionalRetry = Union[retries.Retry, object, None] # type: ignore +try: + from google.api_core import client_logging # type: ignore + + CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER +except ImportError: # pragma: NO COVER + CLIENT_LOGGING_SUPPORTED = False + +_LOGGER = logging.getLogger(__name__) DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version, @@ -145,8 +152,8 @@ def post_update_topic(self, response): """ def pre_create_topic( - self, request: pubsub.Topic, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[pubsub.Topic, Sequence[Tuple[str, str]]]: + self, request: pubsub.Topic, metadata: Sequence[Tuple[str, Union[str, bytes]]] + ) -> Tuple[pubsub.Topic, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for create_topic Override in a subclass to manipulate the request or metadata @@ -157,15 +164,38 @@ def pre_create_topic( def post_create_topic(self, response: pubsub.Topic) -> pubsub.Topic: """Post-rpc interceptor for create_topic - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_create_topic_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Publisher server but before - it is returned to user code. + it is returned to user code. This `post_create_topic` interceptor runs + before the `post_create_topic_with_metadata` interceptor. """ return response + def post_create_topic_with_metadata( + self, response: pubsub.Topic, metadata: Sequence[Tuple[str, Union[str, bytes]]] + ) -> Tuple[pubsub.Topic, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for create_topic + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Publisher server but before it is returned to user code. + + We recommend only using this `post_create_topic_with_metadata` + interceptor in new development instead of the `post_create_topic` interceptor. + When both interceptors are used, this `post_create_topic_with_metadata` interceptor runs after the + `post_create_topic` interceptor. The (possibly modified) response returned by + `post_create_topic` will be passed to + `post_create_topic_with_metadata`. + """ + return response, metadata + def pre_delete_topic( - self, request: pubsub.DeleteTopicRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[pubsub.DeleteTopicRequest, Sequence[Tuple[str, str]]]: + self, + request: pubsub.DeleteTopicRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.DeleteTopicRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for delete_topic Override in a subclass to manipulate the request or metadata @@ -176,8 +206,10 @@ def pre_delete_topic( def pre_detach_subscription( self, request: pubsub.DetachSubscriptionRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[pubsub.DetachSubscriptionRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + pubsub.DetachSubscriptionRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for detach_subscription Override in a subclass to manipulate the request or metadata @@ -190,15 +222,42 @@ def post_detach_subscription( ) -> pubsub.DetachSubscriptionResponse: """Post-rpc interceptor for detach_subscription - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_detach_subscription_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Publisher server but before - it is returned to user code. + it is returned to user code. This `post_detach_subscription` interceptor runs + before the `post_detach_subscription_with_metadata` interceptor. """ return response + def post_detach_subscription_with_metadata( + self, + response: pubsub.DetachSubscriptionResponse, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + pubsub.DetachSubscriptionResponse, Sequence[Tuple[str, Union[str, bytes]]] + ]: + """Post-rpc interceptor for detach_subscription + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Publisher server but before it is returned to user code. + + We recommend only using this `post_detach_subscription_with_metadata` + interceptor in new development instead of the `post_detach_subscription` interceptor. + When both interceptors are used, this `post_detach_subscription_with_metadata` interceptor runs after the + `post_detach_subscription` interceptor. The (possibly modified) response returned by + `post_detach_subscription` will be passed to + `post_detach_subscription_with_metadata`. + """ + return response, metadata + def pre_get_topic( - self, request: pubsub.GetTopicRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[pubsub.GetTopicRequest, Sequence[Tuple[str, str]]]: + self, + request: pubsub.GetTopicRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.GetTopicRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for get_topic Override in a subclass to manipulate the request or metadata @@ -209,15 +268,38 @@ def pre_get_topic( def post_get_topic(self, response: pubsub.Topic) -> pubsub.Topic: """Post-rpc interceptor for get_topic - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_get_topic_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Publisher server but before - it is returned to user code. + it is returned to user code. This `post_get_topic` interceptor runs + before the `post_get_topic_with_metadata` interceptor. """ return response + def post_get_topic_with_metadata( + self, response: pubsub.Topic, metadata: Sequence[Tuple[str, Union[str, bytes]]] + ) -> Tuple[pubsub.Topic, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for get_topic + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Publisher server but before it is returned to user code. + + We recommend only using this `post_get_topic_with_metadata` + interceptor in new development instead of the `post_get_topic` interceptor. + When both interceptors are used, this `post_get_topic_with_metadata` interceptor runs after the + `post_get_topic` interceptor. The (possibly modified) response returned by + `post_get_topic` will be passed to + `post_get_topic_with_metadata`. + """ + return response, metadata + def pre_list_topics( - self, request: pubsub.ListTopicsRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[pubsub.ListTopicsRequest, Sequence[Tuple[str, str]]]: + self, + request: pubsub.ListTopicsRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.ListTopicsRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for list_topics Override in a subclass to manipulate the request or metadata @@ -230,17 +312,42 @@ def post_list_topics( ) -> pubsub.ListTopicsResponse: """Post-rpc interceptor for list_topics - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_list_topics_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Publisher server but before - it is returned to user code. + it is returned to user code. This `post_list_topics` interceptor runs + before the `post_list_topics_with_metadata` interceptor. """ return response + def post_list_topics_with_metadata( + self, + response: pubsub.ListTopicsResponse, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.ListTopicsResponse, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for list_topics + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Publisher server but before it is returned to user code. + + We recommend only using this `post_list_topics_with_metadata` + interceptor in new development instead of the `post_list_topics` interceptor. + When both interceptors are used, this `post_list_topics_with_metadata` interceptor runs after the + `post_list_topics` interceptor. The (possibly modified) response returned by + `post_list_topics` will be passed to + `post_list_topics_with_metadata`. + """ + return response, metadata + def pre_list_topic_snapshots( self, request: pubsub.ListTopicSnapshotsRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[pubsub.ListTopicSnapshotsRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + pubsub.ListTopicSnapshotsRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for list_topic_snapshots Override in a subclass to manipulate the request or metadata @@ -253,17 +360,44 @@ def post_list_topic_snapshots( ) -> pubsub.ListTopicSnapshotsResponse: """Post-rpc interceptor for list_topic_snapshots - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_list_topic_snapshots_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Publisher server but before - it is returned to user code. + it is returned to user code. This `post_list_topic_snapshots` interceptor runs + before the `post_list_topic_snapshots_with_metadata` interceptor. """ return response + def post_list_topic_snapshots_with_metadata( + self, + response: pubsub.ListTopicSnapshotsResponse, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + pubsub.ListTopicSnapshotsResponse, Sequence[Tuple[str, Union[str, bytes]]] + ]: + """Post-rpc interceptor for list_topic_snapshots + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Publisher server but before it is returned to user code. + + We recommend only using this `post_list_topic_snapshots_with_metadata` + interceptor in new development instead of the `post_list_topic_snapshots` interceptor. + When both interceptors are used, this `post_list_topic_snapshots_with_metadata` interceptor runs after the + `post_list_topic_snapshots` interceptor. The (possibly modified) response returned by + `post_list_topic_snapshots` will be passed to + `post_list_topic_snapshots_with_metadata`. + """ + return response, metadata + def pre_list_topic_subscriptions( self, request: pubsub.ListTopicSubscriptionsRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[pubsub.ListTopicSubscriptionsRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + pubsub.ListTopicSubscriptionsRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for list_topic_subscriptions Override in a subclass to manipulate the request or metadata @@ -276,15 +410,42 @@ def post_list_topic_subscriptions( ) -> pubsub.ListTopicSubscriptionsResponse: """Post-rpc interceptor for list_topic_subscriptions - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_list_topic_subscriptions_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Publisher server but before - it is returned to user code. + it is returned to user code. This `post_list_topic_subscriptions` interceptor runs + before the `post_list_topic_subscriptions_with_metadata` interceptor. """ return response + def post_list_topic_subscriptions_with_metadata( + self, + response: pubsub.ListTopicSubscriptionsResponse, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + pubsub.ListTopicSubscriptionsResponse, Sequence[Tuple[str, Union[str, bytes]]] + ]: + """Post-rpc interceptor for list_topic_subscriptions + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Publisher server but before it is returned to user code. + + We recommend only using this `post_list_topic_subscriptions_with_metadata` + interceptor in new development instead of the `post_list_topic_subscriptions` interceptor. + When both interceptors are used, this `post_list_topic_subscriptions_with_metadata` interceptor runs after the + `post_list_topic_subscriptions` interceptor. The (possibly modified) response returned by + `post_list_topic_subscriptions` will be passed to + `post_list_topic_subscriptions_with_metadata`. + """ + return response, metadata + def pre_publish( - self, request: pubsub.PublishRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[pubsub.PublishRequest, Sequence[Tuple[str, str]]]: + self, + request: pubsub.PublishRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.PublishRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for publish Override in a subclass to manipulate the request or metadata @@ -295,15 +456,40 @@ def pre_publish( def post_publish(self, response: pubsub.PublishResponse) -> pubsub.PublishResponse: """Post-rpc interceptor for publish - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_publish_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Publisher server but before - it is returned to user code. + it is returned to user code. This `post_publish` interceptor runs + before the `post_publish_with_metadata` interceptor. """ return response + def post_publish_with_metadata( + self, + response: pubsub.PublishResponse, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.PublishResponse, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for publish + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Publisher server but before it is returned to user code. + + We recommend only using this `post_publish_with_metadata` + interceptor in new development instead of the `post_publish` interceptor. + When both interceptors are used, this `post_publish_with_metadata` interceptor runs after the + `post_publish` interceptor. The (possibly modified) response returned by + `post_publish` will be passed to + `post_publish_with_metadata`. + """ + return response, metadata + def pre_update_topic( - self, request: pubsub.UpdateTopicRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[pubsub.UpdateTopicRequest, Sequence[Tuple[str, str]]]: + self, + request: pubsub.UpdateTopicRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.UpdateTopicRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for update_topic Override in a subclass to manipulate the request or metadata @@ -314,17 +500,40 @@ def pre_update_topic( def post_update_topic(self, response: pubsub.Topic) -> pubsub.Topic: """Post-rpc interceptor for update_topic - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_update_topic_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Publisher server but before - it is returned to user code. + it is returned to user code. This `post_update_topic` interceptor runs + before the `post_update_topic_with_metadata` interceptor. """ return response + def post_update_topic_with_metadata( + self, response: pubsub.Topic, metadata: Sequence[Tuple[str, Union[str, bytes]]] + ) -> Tuple[pubsub.Topic, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for update_topic + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Publisher server but before it is returned to user code. + + We recommend only using this `post_update_topic_with_metadata` + interceptor in new development instead of the `post_update_topic` interceptor. + When both interceptors are used, this `post_update_topic_with_metadata` interceptor runs after the + `post_update_topic` interceptor. The (possibly modified) response returned by + `post_update_topic` will be passed to + `post_update_topic_with_metadata`. + """ + return response, metadata + def pre_get_iam_policy( self, request: iam_policy_pb2.GetIamPolicyRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[iam_policy_pb2.GetIamPolicyRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + iam_policy_pb2.GetIamPolicyRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for get_iam_policy Override in a subclass to manipulate the request or metadata @@ -344,8 +553,10 @@ def post_get_iam_policy(self, response: policy_pb2.Policy) -> policy_pb2.Policy: def pre_set_iam_policy( self, request: iam_policy_pb2.SetIamPolicyRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[iam_policy_pb2.SetIamPolicyRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + iam_policy_pb2.SetIamPolicyRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for set_iam_policy Override in a subclass to manipulate the request or metadata @@ -365,8 +576,11 @@ def post_set_iam_policy(self, response: policy_pb2.Policy) -> policy_pb2.Policy: def pre_test_iam_permissions( self, request: iam_policy_pb2.TestIamPermissionsRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[iam_policy_pb2.TestIamPermissionsRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + iam_policy_pb2.TestIamPermissionsRequest, + Sequence[Tuple[str, Union[str, bytes]]], + ]: """Pre-rpc interceptor for test_iam_permissions Override in a subclass to manipulate the request or metadata @@ -506,7 +720,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Topic: r"""Call the create topic method over HTTP. @@ -516,8 +730,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.Topic: @@ -527,6 +743,7 @@ def __call__( http_options = ( _BasePublisherRestTransport._BaseCreateTopic._get_http_options() ) + request, metadata = self._interceptor.pre_create_topic(request, metadata) transcoded_request = ( _BasePublisherRestTransport._BaseCreateTopic._get_transcoded_request( @@ -545,6 +762,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.PublisherClient.CreateTopic", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "CreateTopic", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = PublisherRestTransport._CreateTopic._get_response( self._host, @@ -566,7 +810,33 @@ def __call__( pb_resp = pubsub.Topic.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_create_topic(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_create_topic_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.Topic.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.PublisherClient.create_topic", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "CreateTopic", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _DeleteTopic(_BasePublisherRestTransport._BaseDeleteTopic, PublisherRestStub): @@ -601,7 +871,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ): r"""Call the delete topic method over HTTP. @@ -611,13 +881,16 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ http_options = ( _BasePublisherRestTransport._BaseDeleteTopic._get_http_options() ) + request, metadata = self._interceptor.pre_delete_topic(request, metadata) transcoded_request = ( _BasePublisherRestTransport._BaseDeleteTopic._get_transcoded_request( @@ -632,6 +905,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.PublisherClient.DeleteTopic", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "DeleteTopic", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = PublisherRestTransport._DeleteTopic._get_response( self._host, @@ -681,7 +981,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.DetachSubscriptionResponse: r"""Call the detach subscription method over HTTP. @@ -692,8 +992,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.DetachSubscriptionResponse: @@ -705,6 +1007,7 @@ def __call__( http_options = ( _BasePublisherRestTransport._BaseDetachSubscription._get_http_options() ) + request, metadata = self._interceptor.pre_detach_subscription( request, metadata ) @@ -717,6 +1020,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.PublisherClient.DetachSubscription", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "DetachSubscription", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = PublisherRestTransport._DetachSubscription._get_response( self._host, @@ -737,7 +1067,35 @@ def __call__( pb_resp = pubsub.DetachSubscriptionResponse.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_detach_subscription(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_detach_subscription_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.DetachSubscriptionResponse.to_json( + response + ) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.PublisherClient.detach_subscription", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "DetachSubscription", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _GetTopic(_BasePublisherRestTransport._BaseGetTopic, PublisherRestStub): @@ -772,7 +1130,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Topic: r"""Call the get topic method over HTTP. @@ -782,8 +1140,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.Topic: @@ -791,6 +1151,7 @@ def __call__( """ http_options = _BasePublisherRestTransport._BaseGetTopic._get_http_options() + request, metadata = self._interceptor.pre_get_topic(request, metadata) transcoded_request = ( _BasePublisherRestTransport._BaseGetTopic._get_transcoded_request( @@ -805,6 +1166,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.PublisherClient.GetTopic", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "GetTopic", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = PublisherRestTransport._GetTopic._get_response( self._host, @@ -825,7 +1213,33 @@ def __call__( pb_resp = pubsub.Topic.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_get_topic(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_get_topic_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.Topic.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.PublisherClient.get_topic", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "GetTopic", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _ListTopics(_BasePublisherRestTransport._BaseListTopics, PublisherRestStub): @@ -860,7 +1274,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.ListTopicsResponse: r"""Call the list topics method over HTTP. @@ -870,8 +1284,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.ListTopicsResponse: @@ -881,6 +1297,7 @@ def __call__( http_options = ( _BasePublisherRestTransport._BaseListTopics._get_http_options() ) + request, metadata = self._interceptor.pre_list_topics(request, metadata) transcoded_request = ( _BasePublisherRestTransport._BaseListTopics._get_transcoded_request( @@ -895,6 +1312,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.PublisherClient.ListTopics", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "ListTopics", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = PublisherRestTransport._ListTopics._get_response( self._host, @@ -915,7 +1359,33 @@ def __call__( pb_resp = pubsub.ListTopicsResponse.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_list_topics(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_list_topics_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.ListTopicsResponse.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.PublisherClient.list_topics", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "ListTopics", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _ListTopicSnapshots( @@ -952,7 +1422,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.ListTopicSnapshotsResponse: r"""Call the list topic snapshots method over HTTP. @@ -962,8 +1432,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.ListTopicSnapshotsResponse: @@ -973,6 +1445,7 @@ def __call__( http_options = ( _BasePublisherRestTransport._BaseListTopicSnapshots._get_http_options() ) + request, metadata = self._interceptor.pre_list_topic_snapshots( request, metadata ) @@ -985,6 +1458,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.PublisherClient.ListTopicSnapshots", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "ListTopicSnapshots", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = PublisherRestTransport._ListTopicSnapshots._get_response( self._host, @@ -1005,7 +1505,35 @@ def __call__( pb_resp = pubsub.ListTopicSnapshotsResponse.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_list_topic_snapshots(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_list_topic_snapshots_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.ListTopicSnapshotsResponse.to_json( + response + ) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.PublisherClient.list_topic_snapshots", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "ListTopicSnapshots", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _ListTopicSubscriptions( @@ -1042,7 +1570,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.ListTopicSubscriptionsResponse: r"""Call the list topic subscriptions method over HTTP. @@ -1052,8 +1580,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.ListTopicSubscriptionsResponse: @@ -1063,6 +1593,7 @@ def __call__( http_options = ( _BasePublisherRestTransport._BaseListTopicSubscriptions._get_http_options() ) + request, metadata = self._interceptor.pre_list_topic_subscriptions( request, metadata ) @@ -1075,6 +1606,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.PublisherClient.ListTopicSubscriptions", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "ListTopicSubscriptions", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = PublisherRestTransport._ListTopicSubscriptions._get_response( self._host, @@ -1095,7 +1653,35 @@ def __call__( pb_resp = pubsub.ListTopicSubscriptionsResponse.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_list_topic_subscriptions(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_list_topic_subscriptions_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.ListTopicSubscriptionsResponse.to_json( + response + ) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.PublisherClient.list_topic_subscriptions", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "ListTopicSubscriptions", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _Publish(_BasePublisherRestTransport._BasePublish, PublisherRestStub): @@ -1131,7 +1717,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.PublishResponse: r"""Call the publish method over HTTP. @@ -1141,8 +1727,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.PublishResponse: @@ -1150,6 +1738,7 @@ def __call__( """ http_options = _BasePublisherRestTransport._BasePublish._get_http_options() + request, metadata = self._interceptor.pre_publish(request, metadata) transcoded_request = ( _BasePublisherRestTransport._BasePublish._get_transcoded_request( @@ -1168,6 +1757,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.PublisherClient.Publish", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "Publish", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = PublisherRestTransport._Publish._get_response( self._host, @@ -1189,7 +1805,33 @@ def __call__( pb_resp = pubsub.PublishResponse.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_publish(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_publish_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.PublishResponse.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.PublisherClient.publish", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "Publish", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _UpdateTopic(_BasePublisherRestTransport._BaseUpdateTopic, PublisherRestStub): @@ -1225,7 +1867,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Topic: r"""Call the update topic method over HTTP. @@ -1235,8 +1877,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.Topic: @@ -1246,6 +1890,7 @@ def __call__( http_options = ( _BasePublisherRestTransport._BaseUpdateTopic._get_http_options() ) + request, metadata = self._interceptor.pre_update_topic(request, metadata) transcoded_request = ( _BasePublisherRestTransport._BaseUpdateTopic._get_transcoded_request( @@ -1264,6 +1909,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.PublisherClient.UpdateTopic", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "UpdateTopic", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = PublisherRestTransport._UpdateTopic._get_response( self._host, @@ -1285,7 +1957,33 @@ def __call__( pb_resp = pubsub.Topic.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_update_topic(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_update_topic_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.Topic.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.PublisherClient.update_topic", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "UpdateTopic", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp @property @@ -1394,7 +2092,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Call the get iam policy method over HTTP. @@ -1404,8 +2102,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: policy_pb2.Policy: Response from GetIamPolicy method. @@ -1414,6 +2114,7 @@ def __call__( http_options = ( _BasePublisherRestTransport._BaseGetIamPolicy._get_http_options() ) + request, metadata = self._interceptor.pre_get_iam_policy(request, metadata) transcoded_request = ( _BasePublisherRestTransport._BaseGetIamPolicy._get_transcoded_request( @@ -1428,6 +2129,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.PublisherClient.GetIamPolicy", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "GetIamPolicy", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = PublisherRestTransport._GetIamPolicy._get_response( self._host, @@ -1447,6 +2175,27 @@ def __call__( resp = policy_pb2.Policy() resp = json_format.Parse(content, resp) resp = self._interceptor.post_get_iam_policy(resp) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = json_format.MessageToJson(resp) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.PublisherAsyncClient.GetIamPolicy", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "GetIamPolicy", + "httpResponse": http_response, + "metadata": http_response["headers"], + }, + ) return resp @property @@ -1488,7 +2237,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Call the set iam policy method over HTTP. @@ -1498,8 +2247,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: policy_pb2.Policy: Response from SetIamPolicy method. @@ -1508,6 +2259,7 @@ def __call__( http_options = ( _BasePublisherRestTransport._BaseSetIamPolicy._get_http_options() ) + request, metadata = self._interceptor.pre_set_iam_policy(request, metadata) transcoded_request = ( _BasePublisherRestTransport._BaseSetIamPolicy._get_transcoded_request( @@ -1526,6 +2278,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.PublisherClient.SetIamPolicy", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "SetIamPolicy", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = PublisherRestTransport._SetIamPolicy._get_response( self._host, @@ -1546,6 +2325,27 @@ def __call__( resp = policy_pb2.Policy() resp = json_format.Parse(content, resp) resp = self._interceptor.post_set_iam_policy(resp) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = json_format.MessageToJson(resp) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.PublisherAsyncClient.SetIamPolicy", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "SetIamPolicy", + "httpResponse": http_response, + "metadata": http_response["headers"], + }, + ) return resp @property @@ -1587,7 +2387,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> iam_policy_pb2.TestIamPermissionsResponse: r"""Call the test iam permissions method over HTTP. @@ -1597,8 +2397,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: iam_policy_pb2.TestIamPermissionsResponse: Response from TestIamPermissions method. @@ -1607,6 +2409,7 @@ def __call__( http_options = ( _BasePublisherRestTransport._BaseTestIamPermissions._get_http_options() ) + request, metadata = self._interceptor.pre_test_iam_permissions( request, metadata ) @@ -1623,6 +2426,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.PublisherClient.TestIamPermissions", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "TestIamPermissions", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = PublisherRestTransport._TestIamPermissions._get_response( self._host, @@ -1643,6 +2473,27 @@ def __call__( resp = iam_policy_pb2.TestIamPermissionsResponse() resp = json_format.Parse(content, resp) resp = self._interceptor.post_test_iam_permissions(resp) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = json_format.MessageToJson(resp) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.PublisherAsyncClient.TestIamPermissions", + extra={ + "serviceName": "google.pubsub.v1.Publisher", + "rpcName": "TestIamPermissions", + "httpResponse": http_response, + "metadata": http_response["headers"], + }, + ) return resp @property diff --git a/google/pubsub_v1/services/publisher/transports/rest_base.py b/google/pubsub_v1/services/publisher/transports/rest_base.py index 1fa78cdd9..dad3a91b2 100644 --- a/google/pubsub_v1/services/publisher/transports/rest_base.py +++ b/google/pubsub_v1/services/publisher/transports/rest_base.py @@ -26,8 +26,6 @@ from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union -from google.iam.v1 import iam_policy_pb2 # type: ignore -from google.iam.v1 import policy_pb2 # type: ignore from google.protobuf import empty_pb2 # type: ignore from google.pubsub_v1.types import pubsub diff --git a/google/pubsub_v1/services/schema_service/async_client.py b/google/pubsub_v1/services/schema_service/async_client.py index 2d160b062..88eeb1fba 100644 --- a/google/pubsub_v1/services/schema_service/async_client.py +++ b/google/pubsub_v1/services/schema_service/async_client.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import logging as std_logging from collections import OrderedDict import re from typing import ( @@ -53,6 +54,15 @@ from .transports.grpc_asyncio import SchemaServiceGrpcAsyncIOTransport from .client import SchemaServiceClient +try: + from google.api_core import client_logging # type: ignore + + CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER +except ImportError: # pragma: NO COVER + CLIENT_LOGGING_SUPPORTED = False + +_LOGGER = std_logging.getLogger(__name__) + class SchemaServiceAsyncClient: """Service for doing schema-related operations.""" @@ -258,6 +268,28 @@ def __init__( client_info=client_info, ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + std_logging.DEBUG + ): # pragma: NO COVER + _LOGGER.debug( + "Created client `google.pubsub_v1.SchemaServiceAsyncClient`.", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "universeDomain": getattr( + self._client._transport._credentials, "universe_domain", "" + ), + "credentialsType": f"{type(self._client._transport._credentials).__module__}.{type(self._client._transport._credentials).__qualname__}", + "credentialsInfo": getattr( + self.transport._credentials, "get_cred_info", lambda: None + )(), + } + if hasattr(self._client._transport, "_credentials") + else { + "serviceName": "google.pubsub.v1.SchemaService", + "credentialsType": None, + }, + ) + async def create_schema( self, request: Optional[Union[gp_schema.CreateSchemaRequest, dict]] = None, @@ -267,7 +299,7 @@ async def create_schema( schema_id: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> gp_schema.Schema: r"""Creates a schema. @@ -335,8 +367,10 @@ async def sample_create_schema(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Schema: @@ -345,7 +379,10 @@ async def sample_create_schema(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([parent, schema, schema_id]) + flattened_params = [parent, schema, schema_id] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -399,7 +436,7 @@ async def get_schema( name: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> schema.Schema: r"""Gets a schema. @@ -442,8 +479,10 @@ async def sample_get_schema(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Schema: @@ -452,7 +491,10 @@ async def sample_get_schema(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name]) + flattened_params = [name] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -502,7 +544,7 @@ async def list_schemas( parent: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pagers.ListSchemasAsyncPager: r"""Lists schemas in a project. @@ -546,8 +588,10 @@ async def sample_list_schemas(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.services.schema_service.pagers.ListSchemasAsyncPager: @@ -560,7 +604,10 @@ async def sample_list_schemas(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([parent]) + flattened_params = [parent] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -621,7 +668,7 @@ async def list_schema_revisions( name: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pagers.ListSchemaRevisionsAsyncPager: r"""Lists all schema revisions for the named schema. @@ -665,8 +712,10 @@ async def sample_list_schema_revisions(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.services.schema_service.pagers.ListSchemaRevisionsAsyncPager: @@ -679,7 +728,10 @@ async def sample_list_schema_revisions(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name]) + flattened_params = [name] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -741,7 +793,7 @@ async def commit_schema( schema: Optional[gp_schema.Schema] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> gp_schema.Schema: r"""Commits a new schema revision to an existing schema. @@ -795,8 +847,10 @@ async def sample_commit_schema(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Schema: @@ -805,7 +859,10 @@ async def sample_commit_schema(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name, schema]) + flattened_params = [name, schema] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -858,7 +915,7 @@ async def rollback_schema( revision_id: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> schema.Schema: r"""Creates a new schema revision that is a copy of the provided revision_id. @@ -913,8 +970,10 @@ async def sample_rollback_schema(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Schema: @@ -923,7 +982,10 @@ async def sample_rollback_schema(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name, revision_id]) + flattened_params = [name, revision_id] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -976,7 +1038,7 @@ async def delete_schema_revision( revision_id: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> schema.Schema: r"""Deletes a specific schema revision. @@ -1029,8 +1091,10 @@ async def sample_delete_schema_revision(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Schema: @@ -1039,7 +1103,10 @@ async def sample_delete_schema_revision(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name, revision_id]) + flattened_params = [name, revision_id] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1091,7 +1158,7 @@ async def delete_schema( name: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> None: r"""Deletes a schema. @@ -1131,13 +1198,18 @@ async def sample_delete_schema(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name]) + flattened_params = [name] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1185,7 +1257,7 @@ async def validate_schema( schema: Optional[gp_schema.Schema] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> gp_schema.ValidateSchemaResponse: r"""Validates a schema. @@ -1239,8 +1311,10 @@ async def sample_validate_schema(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.ValidateSchemaResponse: @@ -1251,7 +1325,10 @@ async def sample_validate_schema(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([parent, schema]) + flattened_params = [parent, schema] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1302,7 +1379,7 @@ async def validate_message( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> schema.ValidateMessageResponse: r"""Validates a message against a schema. @@ -1339,8 +1416,10 @@ async def sample_validate_message(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.ValidateMessageResponse: @@ -1386,21 +1465,23 @@ async def set_iam_policy( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Sets the IAM access control policy on the specified function. Replaces any existing policy. Args: - request (:class:`~.policy_pb2.SetIamPolicyRequest`): + request (:class:`~.iam_policy_pb2.SetIamPolicyRequest`): The request object. Request message for `SetIamPolicy` method. retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.policy_pb2.Policy: Defines an Identity and Access Management (IAM) policy. @@ -1420,6 +1501,7 @@ async def set_iam_policy( **JSON Example** :: + { "bindings": [ { @@ -1504,22 +1586,24 @@ async def get_iam_policy( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Gets the IAM access control policy for a function. - Returns an empty policy if the function exists and does - not have a policy set. + Returns an empty policy if the function exists and does not have a + policy set. Args: request (:class:`~.iam_policy_pb2.GetIamPolicyRequest`): The request object. Request message for `GetIamPolicy` method. - retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, - should be retried. + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if + any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.policy_pb2.Policy: Defines an Identity and Access Management (IAM) policy. @@ -1624,25 +1708,27 @@ async def test_iam_permissions( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> iam_policy_pb2.TestIamPermissionsResponse: - r"""Tests the specified permissions against the IAM access control + r"""Tests the specified IAM permissions against the IAM access control policy for a function. - If the function does not exist, this will - return an empty set of permissions, not a NOT_FOUND error. + If the function does not exist, this will return an empty set + of permissions, not a NOT_FOUND error. Args: request (:class:`~.iam_policy_pb2.TestIamPermissionsRequest`): The request object. Request message for `TestIamPermissions` method. - retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, - should be retried. + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, + if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: - ~iam_policy_pb2.PolicyTestIamPermissionsResponse: + ~.iam_policy_pb2.TestIamPermissionsResponse: Response message for ``TestIamPermissions`` method. """ # Create or coerce a protobuf request object. diff --git a/google/pubsub_v1/services/schema_service/client.py b/google/pubsub_v1/services/schema_service/client.py index c56f8a4f3..07894782c 100644 --- a/google/pubsub_v1/services/schema_service/client.py +++ b/google/pubsub_v1/services/schema_service/client.py @@ -14,6 +14,9 @@ # limitations under the License. # from collections import OrderedDict +from http import HTTPStatus +import json +import logging as std_logging import functools import os import re @@ -49,6 +52,15 @@ except AttributeError: # pragma: NO COVER OptionalRetry = Union[retries.Retry, object, None] # type: ignore +try: + from google.api_core import client_logging # type: ignore + + CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER +except ImportError: # pragma: NO COVER + CLIENT_LOGGING_SUPPORTED = False + +_LOGGER = std_logging.getLogger(__name__) + from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore from google.protobuf import timestamp_pb2 # type: ignore @@ -473,6 +485,33 @@ def _validate_universe_domain(self): # NOTE (b/349488459): universe validation is disabled until further notice. return True + def _add_cred_info_for_auth_errors( + self, error: core_exceptions.GoogleAPICallError + ) -> None: + """Adds credential info string to error details for 401/403/404 errors. + + Args: + error (google.api_core.exceptions.GoogleAPICallError): The error to add the cred info. + """ + if error.code not in [ + HTTPStatus.UNAUTHORIZED, + HTTPStatus.FORBIDDEN, + HTTPStatus.NOT_FOUND, + ]: + return + + cred = self._transport._credentials + + # get_cred_info is only available in google-auth>=2.35.0 + if not hasattr(cred, "get_cred_info"): + return + + # ignore the type check since pypy test fails when get_cred_info + # is not available + cred_info = cred.get_cred_info() # type: ignore + if cred_info and hasattr(error._details, "append"): + error._details.append(json.dumps(cred_info)) + @property def api_endpoint(self): """Return the API endpoint used by the client instance. @@ -577,6 +616,10 @@ def __init__( # Initialize the universe domain validation. self._is_universe_domain_valid = False + if CLIENT_LOGGING_SUPPORTED: # pragma: NO COVER + # Setup logging. + client_logging.initialize_logging() + api_key_value = getattr(self._client_options, "api_key", None) if api_key_value and credentials: raise ValueError( @@ -651,6 +694,29 @@ def __init__( api_audience=self._client_options.api_audience, ) + if "async" not in str(self._transport): + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + std_logging.DEBUG + ): # pragma: NO COVER + _LOGGER.debug( + "Created client `google.pubsub_v1.SchemaServiceClient`.", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "universeDomain": getattr( + self._transport._credentials, "universe_domain", "" + ), + "credentialsType": f"{type(self._transport._credentials).__module__}.{type(self._transport._credentials).__qualname__}", + "credentialsInfo": getattr( + self.transport._credentials, "get_cred_info", lambda: None + )(), + } + if hasattr(self._transport, "_credentials") + else { + "serviceName": "google.pubsub.v1.SchemaService", + "credentialsType": None, + }, + ) + def create_schema( self, request: Optional[Union[gp_schema.CreateSchemaRequest, dict]] = None, @@ -660,7 +726,7 @@ def create_schema( schema_id: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> gp_schema.Schema: r"""Creates a schema. @@ -728,8 +794,10 @@ def sample_create_schema(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Schema: @@ -738,7 +806,10 @@ def sample_create_schema(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([parent, schema, schema_id]) + flattened_params = [parent, schema, schema_id] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -789,7 +860,7 @@ def get_schema( name: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> schema.Schema: r"""Gets a schema. @@ -832,8 +903,10 @@ def sample_get_schema(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Schema: @@ -842,7 +915,10 @@ def sample_get_schema(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name]) + flattened_params = [name] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -889,7 +965,7 @@ def list_schemas( parent: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pagers.ListSchemasPager: r"""Lists schemas in a project. @@ -933,8 +1009,10 @@ def sample_list_schemas(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.services.schema_service.pagers.ListSchemasPager: @@ -947,7 +1025,10 @@ def sample_list_schemas(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([parent]) + flattened_params = [parent] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1005,7 +1086,7 @@ def list_schema_revisions( name: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pagers.ListSchemaRevisionsPager: r"""Lists all schema revisions for the named schema. @@ -1049,8 +1130,10 @@ def sample_list_schema_revisions(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.services.schema_service.pagers.ListSchemaRevisionsPager: @@ -1063,7 +1146,10 @@ def sample_list_schema_revisions(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name]) + flattened_params = [name] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1122,7 +1208,7 @@ def commit_schema( schema: Optional[gp_schema.Schema] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> gp_schema.Schema: r"""Commits a new schema revision to an existing schema. @@ -1176,8 +1262,10 @@ def sample_commit_schema(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Schema: @@ -1186,7 +1274,10 @@ def sample_commit_schema(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name, schema]) + flattened_params = [name, schema] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1236,7 +1327,7 @@ def rollback_schema( revision_id: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> schema.Schema: r"""Creates a new schema revision that is a copy of the provided revision_id. @@ -1291,8 +1382,10 @@ def sample_rollback_schema(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Schema: @@ -1301,7 +1394,10 @@ def sample_rollback_schema(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name, revision_id]) + flattened_params = [name, revision_id] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1351,7 +1447,7 @@ def delete_schema_revision( revision_id: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> schema.Schema: r"""Deletes a specific schema revision. @@ -1404,8 +1500,10 @@ def sample_delete_schema_revision(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Schema: @@ -1414,7 +1512,10 @@ def sample_delete_schema_revision(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name, revision_id]) + flattened_params = [name, revision_id] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1463,7 +1564,7 @@ def delete_schema( name: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> None: r"""Deletes a schema. @@ -1503,13 +1604,18 @@ def sample_delete_schema(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name]) + flattened_params = [name] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1554,7 +1660,7 @@ def validate_schema( schema: Optional[gp_schema.Schema] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> gp_schema.ValidateSchemaResponse: r"""Validates a schema. @@ -1608,8 +1714,10 @@ def sample_validate_schema(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.ValidateSchemaResponse: @@ -1620,7 +1728,10 @@ def sample_validate_schema(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([parent, schema]) + flattened_params = [parent, schema] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1668,7 +1779,7 @@ def validate_message( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> schema.ValidateMessageResponse: r"""Validates a message against a schema. @@ -1705,8 +1816,10 @@ def sample_validate_message(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.ValidateMessageResponse: @@ -1763,7 +1876,7 @@ def set_iam_policy( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Sets the IAM access control policy on the specified function. @@ -1776,8 +1889,10 @@ def set_iam_policy( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.policy_pb2.Policy: Defines an Identity and Access Management (IAM) policy. @@ -1854,11 +1969,7 @@ def set_iam_policy( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method.wrap_method( - self._transport.set_iam_policy, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._transport._wrapped_methods[self._transport.set_iam_policy] # Certain fields should be provided within the metadata header; # add these here. @@ -1869,16 +1980,20 @@ def set_iam_policy( # Validate the universe domain. self._validate_universe_domain() - # Send the request. - response = rpc( - request, - retry=retry, - timeout=timeout, - metadata=metadata, - ) + try: + # Send the request. + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) - # Done; return the response. - return response + # Done; return the response. + return response + except core_exceptions.GoogleAPICallError as e: + self._add_cred_info_for_auth_errors(e) + raise e def get_iam_policy( self, @@ -1886,7 +2001,7 @@ def get_iam_policy( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Gets the IAM access control policy for a function. @@ -1900,8 +2015,10 @@ def get_iam_policy( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.policy_pb2.Policy: Defines an Identity and Access Management (IAM) policy. @@ -1978,11 +2095,7 @@ def get_iam_policy( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method.wrap_method( - self._transport.get_iam_policy, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._transport._wrapped_methods[self._transport.get_iam_policy] # Certain fields should be provided within the metadata header; # add these here. @@ -1993,16 +2106,20 @@ def get_iam_policy( # Validate the universe domain. self._validate_universe_domain() - # Send the request. - response = rpc( - request, - retry=retry, - timeout=timeout, - metadata=metadata, - ) + try: + # Send the request. + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) - # Done; return the response. - return response + # Done; return the response. + return response + except core_exceptions.GoogleAPICallError as e: + self._add_cred_info_for_auth_errors(e) + raise e def test_iam_permissions( self, @@ -2010,7 +2127,7 @@ def test_iam_permissions( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> iam_policy_pb2.TestIamPermissionsResponse: r"""Tests the specified IAM permissions against the IAM access control policy for a function. @@ -2025,8 +2142,10 @@ def test_iam_permissions( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.iam_policy_pb2.TestIamPermissionsResponse: Response message for ``TestIamPermissions`` method. @@ -2040,11 +2159,7 @@ def test_iam_permissions( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method.wrap_method( - self._transport.test_iam_permissions, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._transport._wrapped_methods[self._transport.test_iam_permissions] # Certain fields should be provided within the metadata header; # add these here. @@ -2055,16 +2170,20 @@ def test_iam_permissions( # Validate the universe domain. self._validate_universe_domain() - # Send the request. - response = rpc( - request, - retry=retry, - timeout=timeout, - metadata=metadata, - ) + try: + # Send the request. + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) - # Done; return the response. - return response + # Done; return the response. + return response + except core_exceptions.GoogleAPICallError as e: + self._add_cred_info_for_auth_errors(e) + raise e DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( diff --git a/google/pubsub_v1/services/schema_service/pagers.py b/google/pubsub_v1/services/schema_service/pagers.py index fa42a6b8c..8f7080b26 100644 --- a/google/pubsub_v1/services/schema_service/pagers.py +++ b/google/pubsub_v1/services/schema_service/pagers.py @@ -66,7 +66,7 @@ def __init__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = () + metadata: Sequence[Tuple[str, Union[str, bytes]]] = () ): """Instantiate the pager. @@ -80,8 +80,10 @@ def __init__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ self._method = method self._request = schema.ListSchemasRequest(request) @@ -140,7 +142,7 @@ def __init__( *, retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = () + metadata: Sequence[Tuple[str, Union[str, bytes]]] = () ): """Instantiates the pager. @@ -154,8 +156,10 @@ def __init__( retry (google.api_core.retry.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ self._method = method self._request = schema.ListSchemasRequest(request) @@ -218,7 +222,7 @@ def __init__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = () + metadata: Sequence[Tuple[str, Union[str, bytes]]] = () ): """Instantiate the pager. @@ -232,8 +236,10 @@ def __init__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ self._method = method self._request = schema.ListSchemaRevisionsRequest(request) @@ -292,7 +298,7 @@ def __init__( *, retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = () + metadata: Sequence[Tuple[str, Union[str, bytes]]] = () ): """Instantiates the pager. @@ -306,8 +312,10 @@ def __init__( retry (google.api_core.retry.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ self._method = method self._request = schema.ListSchemaRevisionsRequest(request) diff --git a/google/pubsub_v1/services/schema_service/transports/grpc.py b/google/pubsub_v1/services/schema_service/transports/grpc.py index 421879d19..daabd41b9 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc.py @@ -13,6 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import json +import logging as std_logging +import pickle import warnings from typing import Callable, Dict, Optional, Sequence, Tuple, Union @@ -21,8 +24,11 @@ import google.auth # type: ignore from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore +from google.protobuf.json_format import MessageToJson +import google.protobuf.message import grpc # type: ignore +import proto # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -31,6 +37,81 @@ from google.pubsub_v1.types import schema as gp_schema from .base import SchemaServiceTransport, DEFAULT_CLIENT_INFO +try: + from google.api_core import client_logging # type: ignore + + CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER +except ImportError: # pragma: NO COVER + CLIENT_LOGGING_SUPPORTED = False + +_LOGGER = std_logging.getLogger(__name__) + + +class _LoggingClientInterceptor(grpc.UnaryUnaryClientInterceptor): # pragma: NO COVER + def intercept_unary_unary(self, continuation, client_call_details, request): + logging_enabled = CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + std_logging.DEBUG + ) + if logging_enabled: # pragma: NO COVER + request_metadata = client_call_details.metadata + if isinstance(request, proto.Message): + request_payload = type(request).to_json(request) + elif isinstance(request, google.protobuf.message.Message): + request_payload = MessageToJson(request) + else: + request_payload = f"{type(request).__name__}: {pickle.dumps(request)}" + + request_metadata = { + key: value.decode("utf-8") if isinstance(value, bytes) else value + for key, value in request_metadata + } + grpc_request = { + "payload": request_payload, + "requestMethod": "grpc", + "metadata": dict(request_metadata), + } + _LOGGER.debug( + f"Sending request for {client_call_details.method}", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": client_call_details.method, + "request": grpc_request, + "metadata": grpc_request["metadata"], + }, + ) + + response = continuation(client_call_details, request) + if logging_enabled: # pragma: NO COVER + response_metadata = response.trailing_metadata() + # Convert gRPC metadata `` to list of tuples + metadata = ( + dict([(k, str(v)) for k, v in response_metadata]) + if response_metadata + else None + ) + result = response.result() + if isinstance(result, proto.Message): + response_payload = type(result).to_json(result) + elif isinstance(result, google.protobuf.message.Message): + response_payload = MessageToJson(result) + else: + response_payload = f"{type(result).__name__}: {pickle.dumps(result)}" + grpc_response = { + "payload": response_payload, + "metadata": metadata, + "status": "OK", + } + _LOGGER.debug( + f"Received response for {client_call_details.method}.", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": client_call_details.method, + "response": grpc_response, + "metadata": grpc_response["metadata"], + }, + ) + return response + class SchemaServiceGrpcTransport(SchemaServiceTransport): """gRPC backend transport for SchemaService. @@ -186,7 +267,12 @@ def __init__( ], ) - # Wrap messages. This must be done after self._grpc_channel exists + self._interceptor = _LoggingClientInterceptor() + self._logged_channel = grpc.intercept_channel( + self._grpc_channel, self._interceptor + ) + + # Wrap messages. This must be done after self._logged_channel exists self._prep_wrapped_messages(client_info) @classmethod @@ -260,7 +346,7 @@ def create_schema( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "create_schema" not in self._stubs: - self._stubs["create_schema"] = self.grpc_channel.unary_unary( + self._stubs["create_schema"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/CreateSchema", request_serializer=gp_schema.CreateSchemaRequest.serialize, response_deserializer=gp_schema.Schema.deserialize, @@ -284,7 +370,7 @@ def get_schema(self) -> Callable[[schema.GetSchemaRequest], schema.Schema]: # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "get_schema" not in self._stubs: - self._stubs["get_schema"] = self.grpc_channel.unary_unary( + self._stubs["get_schema"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/GetSchema", request_serializer=schema.GetSchemaRequest.serialize, response_deserializer=schema.Schema.deserialize, @@ -310,7 +396,7 @@ def list_schemas( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "list_schemas" not in self._stubs: - self._stubs["list_schemas"] = self.grpc_channel.unary_unary( + self._stubs["list_schemas"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/ListSchemas", request_serializer=schema.ListSchemasRequest.serialize, response_deserializer=schema.ListSchemasResponse.deserialize, @@ -338,7 +424,7 @@ def list_schema_revisions( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "list_schema_revisions" not in self._stubs: - self._stubs["list_schema_revisions"] = self.grpc_channel.unary_unary( + self._stubs["list_schema_revisions"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/ListSchemaRevisions", request_serializer=schema.ListSchemaRevisionsRequest.serialize, response_deserializer=schema.ListSchemaRevisionsResponse.deserialize, @@ -364,7 +450,7 @@ def commit_schema( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "commit_schema" not in self._stubs: - self._stubs["commit_schema"] = self.grpc_channel.unary_unary( + self._stubs["commit_schema"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/CommitSchema", request_serializer=gp_schema.CommitSchemaRequest.serialize, response_deserializer=gp_schema.Schema.deserialize, @@ -391,7 +477,7 @@ def rollback_schema( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "rollback_schema" not in self._stubs: - self._stubs["rollback_schema"] = self.grpc_channel.unary_unary( + self._stubs["rollback_schema"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/RollbackSchema", request_serializer=schema.RollbackSchemaRequest.serialize, response_deserializer=schema.Schema.deserialize, @@ -417,7 +503,7 @@ def delete_schema_revision( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "delete_schema_revision" not in self._stubs: - self._stubs["delete_schema_revision"] = self.grpc_channel.unary_unary( + self._stubs["delete_schema_revision"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/DeleteSchemaRevision", request_serializer=schema.DeleteSchemaRevisionRequest.serialize, response_deserializer=schema.Schema.deserialize, @@ -441,7 +527,7 @@ def delete_schema(self) -> Callable[[schema.DeleteSchemaRequest], empty_pb2.Empt # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "delete_schema" not in self._stubs: - self._stubs["delete_schema"] = self.grpc_channel.unary_unary( + self._stubs["delete_schema"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/DeleteSchema", request_serializer=schema.DeleteSchemaRequest.serialize, response_deserializer=empty_pb2.Empty.FromString, @@ -467,7 +553,7 @@ def validate_schema( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "validate_schema" not in self._stubs: - self._stubs["validate_schema"] = self.grpc_channel.unary_unary( + self._stubs["validate_schema"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/ValidateSchema", request_serializer=gp_schema.ValidateSchemaRequest.serialize, response_deserializer=gp_schema.ValidateSchemaResponse.deserialize, @@ -493,13 +579,16 @@ def validate_message( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "validate_message" not in self._stubs: - self._stubs["validate_message"] = self.grpc_channel.unary_unary( + self._stubs["validate_message"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/ValidateMessage", request_serializer=schema.ValidateMessageRequest.serialize, response_deserializer=schema.ValidateMessageResponse.deserialize, ) return self._stubs["validate_message"] + def close(self): + self._logged_channel.close() + @property def set_iam_policy( self, @@ -518,7 +607,7 @@ def set_iam_policy( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "set_iam_policy" not in self._stubs: - self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( + self._stubs["set_iam_policy"] = self._logged_channel.unary_unary( "/google.iam.v1.IAMPolicy/SetIamPolicy", request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, response_deserializer=policy_pb2.Policy.FromString, @@ -544,7 +633,7 @@ def get_iam_policy( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "get_iam_policy" not in self._stubs: - self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( + self._stubs["get_iam_policy"] = self._logged_channel.unary_unary( "/google.iam.v1.IAMPolicy/GetIamPolicy", request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, response_deserializer=policy_pb2.Policy.FromString, @@ -573,16 +662,13 @@ def test_iam_permissions( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "test_iam_permissions" not in self._stubs: - self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( + self._stubs["test_iam_permissions"] = self._logged_channel.unary_unary( "/google.iam.v1.IAMPolicy/TestIamPermissions", request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, ) return self._stubs["test_iam_permissions"] - def close(self): - self.grpc_channel.close() - @property def kind(self) -> str: return "grpc" diff --git a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py index e642ed1b9..c321b88c2 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py @@ -14,6 +14,9 @@ # limitations under the License. # import inspect +import json +import pickle +import logging as std_logging import warnings from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union @@ -23,8 +26,11 @@ from google.api_core import retry_async as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore +from google.protobuf.json_format import MessageToJson +import google.protobuf.message import grpc # type: ignore +import proto # type: ignore from grpc.experimental import aio # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore @@ -35,6 +41,82 @@ from .base import SchemaServiceTransport, DEFAULT_CLIENT_INFO from .grpc import SchemaServiceGrpcTransport +try: + from google.api_core import client_logging # type: ignore + + CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER +except ImportError: # pragma: NO COVER + CLIENT_LOGGING_SUPPORTED = False + +_LOGGER = std_logging.getLogger(__name__) + + +class _LoggingClientAIOInterceptor( + grpc.aio.UnaryUnaryClientInterceptor +): # pragma: NO COVER + async def intercept_unary_unary(self, continuation, client_call_details, request): + logging_enabled = CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + std_logging.DEBUG + ) + if logging_enabled: # pragma: NO COVER + request_metadata = client_call_details.metadata + if isinstance(request, proto.Message): + request_payload = type(request).to_json(request) + elif isinstance(request, google.protobuf.message.Message): + request_payload = MessageToJson(request) + else: + request_payload = f"{type(request).__name__}: {pickle.dumps(request)}" + + request_metadata = { + key: value.decode("utf-8") if isinstance(value, bytes) else value + for key, value in request_metadata + } + grpc_request = { + "payload": request_payload, + "requestMethod": "grpc", + "metadata": dict(request_metadata), + } + _LOGGER.debug( + f"Sending request for {client_call_details.method}", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": str(client_call_details.method), + "request": grpc_request, + "metadata": grpc_request["metadata"], + }, + ) + response = await continuation(client_call_details, request) + if logging_enabled: # pragma: NO COVER + response_metadata = await response.trailing_metadata() + # Convert gRPC metadata `` to list of tuples + metadata = ( + dict([(k, str(v)) for k, v in response_metadata]) + if response_metadata + else None + ) + result = await response + if isinstance(result, proto.Message): + response_payload = type(result).to_json(result) + elif isinstance(result, google.protobuf.message.Message): + response_payload = MessageToJson(result) + else: + response_payload = f"{type(result).__name__}: {pickle.dumps(result)}" + grpc_response = { + "payload": response_payload, + "metadata": metadata, + "status": "OK", + } + _LOGGER.debug( + f"Received response to rpc {client_call_details.method}.", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": str(client_call_details.method), + "response": grpc_response, + "metadata": grpc_response["metadata"], + }, + ) + return response + class SchemaServiceGrpcAsyncIOTransport(SchemaServiceTransport): """gRPC AsyncIO backend transport for SchemaService. @@ -233,10 +315,13 @@ def __init__( ], ) - # Wrap messages. This must be done after self._grpc_channel exists + self._interceptor = _LoggingClientAIOInterceptor() + self._grpc_channel._unary_unary_interceptors.append(self._interceptor) + self._logged_channel = self._grpc_channel self._wrap_with_kind = ( "kind" in inspect.signature(gapic_v1.method_async.wrap_method).parameters ) + # Wrap messages. This must be done after self._logged_channel exists self._prep_wrapped_messages(client_info) @property @@ -268,7 +353,7 @@ def create_schema( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "create_schema" not in self._stubs: - self._stubs["create_schema"] = self.grpc_channel.unary_unary( + self._stubs["create_schema"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/CreateSchema", request_serializer=gp_schema.CreateSchemaRequest.serialize, response_deserializer=gp_schema.Schema.deserialize, @@ -294,7 +379,7 @@ def get_schema( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "get_schema" not in self._stubs: - self._stubs["get_schema"] = self.grpc_channel.unary_unary( + self._stubs["get_schema"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/GetSchema", request_serializer=schema.GetSchemaRequest.serialize, response_deserializer=schema.Schema.deserialize, @@ -320,7 +405,7 @@ def list_schemas( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "list_schemas" not in self._stubs: - self._stubs["list_schemas"] = self.grpc_channel.unary_unary( + self._stubs["list_schemas"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/ListSchemas", request_serializer=schema.ListSchemasRequest.serialize, response_deserializer=schema.ListSchemasResponse.deserialize, @@ -349,7 +434,7 @@ def list_schema_revisions( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "list_schema_revisions" not in self._stubs: - self._stubs["list_schema_revisions"] = self.grpc_channel.unary_unary( + self._stubs["list_schema_revisions"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/ListSchemaRevisions", request_serializer=schema.ListSchemaRevisionsRequest.serialize, response_deserializer=schema.ListSchemaRevisionsResponse.deserialize, @@ -375,7 +460,7 @@ def commit_schema( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "commit_schema" not in self._stubs: - self._stubs["commit_schema"] = self.grpc_channel.unary_unary( + self._stubs["commit_schema"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/CommitSchema", request_serializer=gp_schema.CommitSchemaRequest.serialize, response_deserializer=gp_schema.Schema.deserialize, @@ -402,7 +487,7 @@ def rollback_schema( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "rollback_schema" not in self._stubs: - self._stubs["rollback_schema"] = self.grpc_channel.unary_unary( + self._stubs["rollback_schema"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/RollbackSchema", request_serializer=schema.RollbackSchemaRequest.serialize, response_deserializer=schema.Schema.deserialize, @@ -428,7 +513,7 @@ def delete_schema_revision( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "delete_schema_revision" not in self._stubs: - self._stubs["delete_schema_revision"] = self.grpc_channel.unary_unary( + self._stubs["delete_schema_revision"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/DeleteSchemaRevision", request_serializer=schema.DeleteSchemaRevisionRequest.serialize, response_deserializer=schema.Schema.deserialize, @@ -454,7 +539,7 @@ def delete_schema( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "delete_schema" not in self._stubs: - self._stubs["delete_schema"] = self.grpc_channel.unary_unary( + self._stubs["delete_schema"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/DeleteSchema", request_serializer=schema.DeleteSchemaRequest.serialize, response_deserializer=empty_pb2.Empty.FromString, @@ -482,7 +567,7 @@ def validate_schema( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "validate_schema" not in self._stubs: - self._stubs["validate_schema"] = self.grpc_channel.unary_unary( + self._stubs["validate_schema"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/ValidateSchema", request_serializer=gp_schema.ValidateSchemaRequest.serialize, response_deserializer=gp_schema.ValidateSchemaResponse.deserialize, @@ -510,93 +595,13 @@ def validate_message( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "validate_message" not in self._stubs: - self._stubs["validate_message"] = self.grpc_channel.unary_unary( + self._stubs["validate_message"] = self._logged_channel.unary_unary( "/google.pubsub.v1.SchemaService/ValidateMessage", request_serializer=schema.ValidateMessageRequest.serialize, response_deserializer=schema.ValidateMessageResponse.deserialize, ) return self._stubs["validate_message"] - @property - def set_iam_policy( - self, - ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: - r"""Return a callable for the set iam policy method over gRPC. - Sets the IAM access control policy on the specified - function. Replaces any existing policy. - Returns: - Callable[[~.SetIamPolicyRequest], - Awaitable[~.Policy]]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "set_iam_policy" not in self._stubs: - self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( - "/google.iam.v1.IAMPolicy/SetIamPolicy", - request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, - response_deserializer=policy_pb2.Policy.FromString, - ) - return self._stubs["set_iam_policy"] - - @property - def get_iam_policy( - self, - ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: - r"""Return a callable for the get iam policy method over gRPC. - Gets the IAM access control policy for a function. - Returns an empty policy if the function exists and does - not have a policy set. - Returns: - Callable[[~.GetIamPolicyRequest], - Awaitable[~.Policy]]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "get_iam_policy" not in self._stubs: - self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( - "/google.iam.v1.IAMPolicy/GetIamPolicy", - request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, - response_deserializer=policy_pb2.Policy.FromString, - ) - return self._stubs["get_iam_policy"] - - @property - def test_iam_permissions( - self, - ) -> Callable[ - [iam_policy_pb2.TestIamPermissionsRequest], - Awaitable[iam_policy_pb2.TestIamPermissionsResponse], - ]: - r"""Return a callable for the test iam permissions method over gRPC. - Tests the specified permissions against the IAM access control - policy for a function. If the function does not exist, this will - return an empty set of permissions, not a NOT_FOUND error. - Returns: - Callable[[~.TestIamPermissionsRequest], - Awaitable[~.TestIamPermissionsResponse]]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "test_iam_permissions" not in self._stubs: - self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( - "/google.iam.v1.IAMPolicy/TestIamPermissions", - request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, - response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, - ) - return self._stubs["test_iam_permissions"] - def _prep_wrapped_messages(self, client_info): """Precompute the wrapped methods, overriding the base class method to use async wrappers.""" self._wrapped_methods = { @@ -763,11 +768,91 @@ def _wrap_method(self, func, *args, **kwargs): return gapic_v1.method_async.wrap_method(func, *args, **kwargs) def close(self): - return self.grpc_channel.close() + return self._logged_channel.close() @property def kind(self) -> str: return "grpc_asyncio" + @property + def set_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the set iam policy method over gRPC. + Sets the IAM access control policy on the specified + function. Replaces any existing policy. + Returns: + Callable[[~.SetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "set_iam_policy" not in self._stubs: + self._stubs["set_iam_policy"] = self._logged_channel.unary_unary( + "/google.iam.v1.IAMPolicy/SetIamPolicy", + request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["set_iam_policy"] + + @property + def get_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the get iam policy method over gRPC. + Gets the IAM access control policy for a function. + Returns an empty policy if the function exists and does + not have a policy set. + Returns: + Callable[[~.GetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_iam_policy" not in self._stubs: + self._stubs["get_iam_policy"] = self._logged_channel.unary_unary( + "/google.iam.v1.IAMPolicy/GetIamPolicy", + request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["get_iam_policy"] + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + iam_policy_pb2.TestIamPermissionsResponse, + ]: + r"""Return a callable for the test iam permissions method over gRPC. + Tests the specified permissions against the IAM access control + policy for a function. If the function does not exist, this will + return an empty set of permissions, not a NOT_FOUND error. + Returns: + Callable[[~.TestIamPermissionsRequest], + ~.TestIamPermissionsResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "test_iam_permissions" not in self._stubs: + self._stubs["test_iam_permissions"] = self._logged_channel.unary_unary( + "/google.iam.v1.IAMPolicy/TestIamPermissions", + request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, + response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, + ) + return self._stubs["test_iam_permissions"] + __all__ = ("SchemaServiceGrpcAsyncIOTransport",) diff --git a/google/pubsub_v1/services/schema_service/transports/rest.py b/google/pubsub_v1/services/schema_service/transports/rest.py index 7cf86c808..1bed03dc0 100644 --- a/google/pubsub_v1/services/schema_service/transports/rest.py +++ b/google/pubsub_v1/services/schema_service/transports/rest.py @@ -13,9 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import logging +import json # type: ignore from google.auth.transport.requests import AuthorizedSession # type: ignore -import json # type: ignore from google.auth import credentials as ga_credentials # type: ignore from google.api_core import exceptions as core_exceptions from google.api_core import retry as retries @@ -33,8 +34,6 @@ import warnings -from google.iam.v1 import iam_policy_pb2 # type: ignore -from google.iam.v1 import policy_pb2 # type: ignore from google.protobuf import empty_pb2 # type: ignore from google.pubsub_v1.types import schema from google.pubsub_v1.types import schema as gp_schema @@ -48,6 +47,14 @@ except AttributeError: # pragma: NO COVER OptionalRetry = Union[retries.Retry, object, None] # type: ignore +try: + from google.api_core import client_logging # type: ignore + + CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER +except ImportError: # pragma: NO COVER + CLIENT_LOGGING_SUPPORTED = False + +_LOGGER = logging.getLogger(__name__) DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version, @@ -156,8 +163,8 @@ def post_validate_schema(self, response): def pre_commit_schema( self, request: gp_schema.CommitSchemaRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[gp_schema.CommitSchemaRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[gp_schema.CommitSchemaRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for commit_schema Override in a subclass to manipulate the request or metadata @@ -168,17 +175,40 @@ def pre_commit_schema( def post_commit_schema(self, response: gp_schema.Schema) -> gp_schema.Schema: """Post-rpc interceptor for commit_schema - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_commit_schema_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the SchemaService server but before - it is returned to user code. + it is returned to user code. This `post_commit_schema` interceptor runs + before the `post_commit_schema_with_metadata` interceptor. """ return response + def post_commit_schema_with_metadata( + self, + response: gp_schema.Schema, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[gp_schema.Schema, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for commit_schema + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the SchemaService server but before it is returned to user code. + + We recommend only using this `post_commit_schema_with_metadata` + interceptor in new development instead of the `post_commit_schema` interceptor. + When both interceptors are used, this `post_commit_schema_with_metadata` interceptor runs after the + `post_commit_schema` interceptor. The (possibly modified) response returned by + `post_commit_schema` will be passed to + `post_commit_schema_with_metadata`. + """ + return response, metadata + def pre_create_schema( self, request: gp_schema.CreateSchemaRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[gp_schema.CreateSchemaRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[gp_schema.CreateSchemaRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for create_schema Override in a subclass to manipulate the request or metadata @@ -189,15 +219,40 @@ def pre_create_schema( def post_create_schema(self, response: gp_schema.Schema) -> gp_schema.Schema: """Post-rpc interceptor for create_schema - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_create_schema_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the SchemaService server but before - it is returned to user code. + it is returned to user code. This `post_create_schema` interceptor runs + before the `post_create_schema_with_metadata` interceptor. """ return response + def post_create_schema_with_metadata( + self, + response: gp_schema.Schema, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[gp_schema.Schema, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for create_schema + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the SchemaService server but before it is returned to user code. + + We recommend only using this `post_create_schema_with_metadata` + interceptor in new development instead of the `post_create_schema` interceptor. + When both interceptors are used, this `post_create_schema_with_metadata` interceptor runs after the + `post_create_schema` interceptor. The (possibly modified) response returned by + `post_create_schema` will be passed to + `post_create_schema_with_metadata`. + """ + return response, metadata + def pre_delete_schema( - self, request: schema.DeleteSchemaRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[schema.DeleteSchemaRequest, Sequence[Tuple[str, str]]]: + self, + request: schema.DeleteSchemaRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[schema.DeleteSchemaRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for delete_schema Override in a subclass to manipulate the request or metadata @@ -208,8 +263,10 @@ def pre_delete_schema( def pre_delete_schema_revision( self, request: schema.DeleteSchemaRevisionRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[schema.DeleteSchemaRevisionRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + schema.DeleteSchemaRevisionRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for delete_schema_revision Override in a subclass to manipulate the request or metadata @@ -220,15 +277,38 @@ def pre_delete_schema_revision( def post_delete_schema_revision(self, response: schema.Schema) -> schema.Schema: """Post-rpc interceptor for delete_schema_revision - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_delete_schema_revision_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the SchemaService server but before - it is returned to user code. + it is returned to user code. This `post_delete_schema_revision` interceptor runs + before the `post_delete_schema_revision_with_metadata` interceptor. """ return response + def post_delete_schema_revision_with_metadata( + self, response: schema.Schema, metadata: Sequence[Tuple[str, Union[str, bytes]]] + ) -> Tuple[schema.Schema, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for delete_schema_revision + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the SchemaService server but before it is returned to user code. + + We recommend only using this `post_delete_schema_revision_with_metadata` + interceptor in new development instead of the `post_delete_schema_revision` interceptor. + When both interceptors are used, this `post_delete_schema_revision_with_metadata` interceptor runs after the + `post_delete_schema_revision` interceptor. The (possibly modified) response returned by + `post_delete_schema_revision` will be passed to + `post_delete_schema_revision_with_metadata`. + """ + return response, metadata + def pre_get_schema( - self, request: schema.GetSchemaRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[schema.GetSchemaRequest, Sequence[Tuple[str, str]]]: + self, + request: schema.GetSchemaRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[schema.GetSchemaRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for get_schema Override in a subclass to manipulate the request or metadata @@ -239,17 +319,40 @@ def pre_get_schema( def post_get_schema(self, response: schema.Schema) -> schema.Schema: """Post-rpc interceptor for get_schema - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_get_schema_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the SchemaService server but before - it is returned to user code. + it is returned to user code. This `post_get_schema` interceptor runs + before the `post_get_schema_with_metadata` interceptor. """ return response + def post_get_schema_with_metadata( + self, response: schema.Schema, metadata: Sequence[Tuple[str, Union[str, bytes]]] + ) -> Tuple[schema.Schema, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for get_schema + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the SchemaService server but before it is returned to user code. + + We recommend only using this `post_get_schema_with_metadata` + interceptor in new development instead of the `post_get_schema` interceptor. + When both interceptors are used, this `post_get_schema_with_metadata` interceptor runs after the + `post_get_schema` interceptor. The (possibly modified) response returned by + `post_get_schema` will be passed to + `post_get_schema_with_metadata`. + """ + return response, metadata + def pre_list_schema_revisions( self, request: schema.ListSchemaRevisionsRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[schema.ListSchemaRevisionsRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + schema.ListSchemaRevisionsRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for list_schema_revisions Override in a subclass to manipulate the request or metadata @@ -262,15 +365,42 @@ def post_list_schema_revisions( ) -> schema.ListSchemaRevisionsResponse: """Post-rpc interceptor for list_schema_revisions - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_list_schema_revisions_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the SchemaService server but before - it is returned to user code. + it is returned to user code. This `post_list_schema_revisions` interceptor runs + before the `post_list_schema_revisions_with_metadata` interceptor. """ return response + def post_list_schema_revisions_with_metadata( + self, + response: schema.ListSchemaRevisionsResponse, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + schema.ListSchemaRevisionsResponse, Sequence[Tuple[str, Union[str, bytes]]] + ]: + """Post-rpc interceptor for list_schema_revisions + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the SchemaService server but before it is returned to user code. + + We recommend only using this `post_list_schema_revisions_with_metadata` + interceptor in new development instead of the `post_list_schema_revisions` interceptor. + When both interceptors are used, this `post_list_schema_revisions_with_metadata` interceptor runs after the + `post_list_schema_revisions` interceptor. The (possibly modified) response returned by + `post_list_schema_revisions` will be passed to + `post_list_schema_revisions_with_metadata`. + """ + return response, metadata + def pre_list_schemas( - self, request: schema.ListSchemasRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[schema.ListSchemasRequest, Sequence[Tuple[str, str]]]: + self, + request: schema.ListSchemasRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[schema.ListSchemasRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for list_schemas Override in a subclass to manipulate the request or metadata @@ -283,15 +413,40 @@ def post_list_schemas( ) -> schema.ListSchemasResponse: """Post-rpc interceptor for list_schemas - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_list_schemas_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the SchemaService server but before - it is returned to user code. + it is returned to user code. This `post_list_schemas` interceptor runs + before the `post_list_schemas_with_metadata` interceptor. """ return response + def post_list_schemas_with_metadata( + self, + response: schema.ListSchemasResponse, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[schema.ListSchemasResponse, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for list_schemas + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the SchemaService server but before it is returned to user code. + + We recommend only using this `post_list_schemas_with_metadata` + interceptor in new development instead of the `post_list_schemas` interceptor. + When both interceptors are used, this `post_list_schemas_with_metadata` interceptor runs after the + `post_list_schemas` interceptor. The (possibly modified) response returned by + `post_list_schemas` will be passed to + `post_list_schemas_with_metadata`. + """ + return response, metadata + def pre_rollback_schema( - self, request: schema.RollbackSchemaRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[schema.RollbackSchemaRequest, Sequence[Tuple[str, str]]]: + self, + request: schema.RollbackSchemaRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[schema.RollbackSchemaRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for rollback_schema Override in a subclass to manipulate the request or metadata @@ -302,17 +457,38 @@ def pre_rollback_schema( def post_rollback_schema(self, response: schema.Schema) -> schema.Schema: """Post-rpc interceptor for rollback_schema - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_rollback_schema_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the SchemaService server but before - it is returned to user code. + it is returned to user code. This `post_rollback_schema` interceptor runs + before the `post_rollback_schema_with_metadata` interceptor. """ return response + def post_rollback_schema_with_metadata( + self, response: schema.Schema, metadata: Sequence[Tuple[str, Union[str, bytes]]] + ) -> Tuple[schema.Schema, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for rollback_schema + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the SchemaService server but before it is returned to user code. + + We recommend only using this `post_rollback_schema_with_metadata` + interceptor in new development instead of the `post_rollback_schema` interceptor. + When both interceptors are used, this `post_rollback_schema_with_metadata` interceptor runs after the + `post_rollback_schema` interceptor. The (possibly modified) response returned by + `post_rollback_schema` will be passed to + `post_rollback_schema_with_metadata`. + """ + return response, metadata + def pre_validate_message( self, request: schema.ValidateMessageRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[schema.ValidateMessageRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[schema.ValidateMessageRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for validate_message Override in a subclass to manipulate the request or metadata @@ -325,17 +501,42 @@ def post_validate_message( ) -> schema.ValidateMessageResponse: """Post-rpc interceptor for validate_message - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_validate_message_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the SchemaService server but before - it is returned to user code. + it is returned to user code. This `post_validate_message` interceptor runs + before the `post_validate_message_with_metadata` interceptor. """ return response + def post_validate_message_with_metadata( + self, + response: schema.ValidateMessageResponse, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[schema.ValidateMessageResponse, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for validate_message + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the SchemaService server but before it is returned to user code. + + We recommend only using this `post_validate_message_with_metadata` + interceptor in new development instead of the `post_validate_message` interceptor. + When both interceptors are used, this `post_validate_message_with_metadata` interceptor runs after the + `post_validate_message` interceptor. The (possibly modified) response returned by + `post_validate_message` will be passed to + `post_validate_message_with_metadata`. + """ + return response, metadata + def pre_validate_schema( self, request: gp_schema.ValidateSchemaRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[gp_schema.ValidateSchemaRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + gp_schema.ValidateSchemaRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for validate_schema Override in a subclass to manipulate the request or metadata @@ -348,17 +549,44 @@ def post_validate_schema( ) -> gp_schema.ValidateSchemaResponse: """Post-rpc interceptor for validate_schema - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_validate_schema_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the SchemaService server but before - it is returned to user code. + it is returned to user code. This `post_validate_schema` interceptor runs + before the `post_validate_schema_with_metadata` interceptor. """ return response + def post_validate_schema_with_metadata( + self, + response: gp_schema.ValidateSchemaResponse, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + gp_schema.ValidateSchemaResponse, Sequence[Tuple[str, Union[str, bytes]]] + ]: + """Post-rpc interceptor for validate_schema + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the SchemaService server but before it is returned to user code. + + We recommend only using this `post_validate_schema_with_metadata` + interceptor in new development instead of the `post_validate_schema` interceptor. + When both interceptors are used, this `post_validate_schema_with_metadata` interceptor runs after the + `post_validate_schema` interceptor. The (possibly modified) response returned by + `post_validate_schema` will be passed to + `post_validate_schema_with_metadata`. + """ + return response, metadata + def pre_get_iam_policy( self, request: iam_policy_pb2.GetIamPolicyRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[iam_policy_pb2.GetIamPolicyRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + iam_policy_pb2.GetIamPolicyRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for get_iam_policy Override in a subclass to manipulate the request or metadata @@ -378,8 +606,10 @@ def post_get_iam_policy(self, response: policy_pb2.Policy) -> policy_pb2.Policy: def pre_set_iam_policy( self, request: iam_policy_pb2.SetIamPolicyRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[iam_policy_pb2.SetIamPolicyRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + iam_policy_pb2.SetIamPolicyRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for set_iam_policy Override in a subclass to manipulate the request or metadata @@ -399,8 +629,11 @@ def post_set_iam_policy(self, response: policy_pb2.Policy) -> policy_pb2.Policy: def pre_test_iam_permissions( self, request: iam_policy_pb2.TestIamPermissionsRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[iam_policy_pb2.TestIamPermissionsRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + iam_policy_pb2.TestIamPermissionsRequest, + Sequence[Tuple[str, Union[str, bytes]]], + ]: """Pre-rpc interceptor for test_iam_permissions Override in a subclass to manipulate the request or metadata @@ -541,7 +774,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> gp_schema.Schema: r"""Call the commit schema method over HTTP. @@ -551,8 +784,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.gp_schema.Schema: @@ -562,6 +797,7 @@ def __call__( http_options = ( _BaseSchemaServiceRestTransport._BaseCommitSchema._get_http_options() ) + request, metadata = self._interceptor.pre_commit_schema(request, metadata) transcoded_request = _BaseSchemaServiceRestTransport._BaseCommitSchema._get_transcoded_request( http_options, request @@ -576,6 +812,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SchemaServiceClient.CommitSchema", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "CommitSchema", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SchemaServiceRestTransport._CommitSchema._get_response( self._host, @@ -597,7 +860,33 @@ def __call__( pb_resp = gp_schema.Schema.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_commit_schema(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_commit_schema_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = gp_schema.Schema.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SchemaServiceClient.commit_schema", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "CommitSchema", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _CreateSchema( @@ -635,7 +924,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> gp_schema.Schema: r"""Call the create schema method over HTTP. @@ -645,8 +934,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.gp_schema.Schema: @@ -656,6 +947,7 @@ def __call__( http_options = ( _BaseSchemaServiceRestTransport._BaseCreateSchema._get_http_options() ) + request, metadata = self._interceptor.pre_create_schema(request, metadata) transcoded_request = _BaseSchemaServiceRestTransport._BaseCreateSchema._get_transcoded_request( http_options, request @@ -670,6 +962,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SchemaServiceClient.CreateSchema", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "CreateSchema", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SchemaServiceRestTransport._CreateSchema._get_response( self._host, @@ -691,7 +1010,33 @@ def __call__( pb_resp = gp_schema.Schema.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_create_schema(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_create_schema_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = gp_schema.Schema.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SchemaServiceClient.create_schema", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "CreateSchema", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _DeleteSchema( @@ -728,7 +1073,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ): r"""Call the delete schema method over HTTP. @@ -738,13 +1083,16 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ http_options = ( _BaseSchemaServiceRestTransport._BaseDeleteSchema._get_http_options() ) + request, metadata = self._interceptor.pre_delete_schema(request, metadata) transcoded_request = _BaseSchemaServiceRestTransport._BaseDeleteSchema._get_transcoded_request( http_options, request @@ -755,6 +1103,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SchemaServiceClient.DeleteSchema", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "DeleteSchema", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SchemaServiceRestTransport._DeleteSchema._get_response( self._host, @@ -804,7 +1179,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> schema.Schema: r"""Call the delete schema revision method over HTTP. @@ -814,8 +1189,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.schema.Schema: @@ -825,6 +1202,7 @@ def __call__( http_options = ( _BaseSchemaServiceRestTransport._BaseDeleteSchemaRevision._get_http_options() ) + request, metadata = self._interceptor.pre_delete_schema_revision( request, metadata ) @@ -837,6 +1215,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SchemaServiceClient.DeleteSchemaRevision", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "DeleteSchemaRevision", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SchemaServiceRestTransport._DeleteSchemaRevision._get_response( self._host, @@ -857,7 +1262,33 @@ def __call__( pb_resp = schema.Schema.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_delete_schema_revision(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_delete_schema_revision_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = schema.Schema.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SchemaServiceClient.delete_schema_revision", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "DeleteSchemaRevision", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _GetSchema( @@ -894,7 +1325,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> schema.Schema: r"""Call the get schema method over HTTP. @@ -904,8 +1335,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.schema.Schema: @@ -915,6 +1348,7 @@ def __call__( http_options = ( _BaseSchemaServiceRestTransport._BaseGetSchema._get_http_options() ) + request, metadata = self._interceptor.pre_get_schema(request, metadata) transcoded_request = ( _BaseSchemaServiceRestTransport._BaseGetSchema._get_transcoded_request( @@ -929,6 +1363,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SchemaServiceClient.GetSchema", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "GetSchema", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SchemaServiceRestTransport._GetSchema._get_response( self._host, @@ -949,7 +1410,33 @@ def __call__( pb_resp = schema.Schema.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_get_schema(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_get_schema_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = schema.Schema.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SchemaServiceClient.get_schema", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "GetSchema", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _ListSchemaRevisions( @@ -986,7 +1473,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> schema.ListSchemaRevisionsResponse: r"""Call the list schema revisions method over HTTP. @@ -996,8 +1483,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.schema.ListSchemaRevisionsResponse: @@ -1007,6 +1496,7 @@ def __call__( http_options = ( _BaseSchemaServiceRestTransport._BaseListSchemaRevisions._get_http_options() ) + request, metadata = self._interceptor.pre_list_schema_revisions( request, metadata ) @@ -1019,6 +1509,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SchemaServiceClient.ListSchemaRevisions", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "ListSchemaRevisions", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SchemaServiceRestTransport._ListSchemaRevisions._get_response( self._host, @@ -1039,7 +1556,35 @@ def __call__( pb_resp = schema.ListSchemaRevisionsResponse.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_list_schema_revisions(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_list_schema_revisions_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = schema.ListSchemaRevisionsResponse.to_json( + response + ) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SchemaServiceClient.list_schema_revisions", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "ListSchemaRevisions", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _ListSchemas( @@ -1076,7 +1621,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> schema.ListSchemasResponse: r"""Call the list schemas method over HTTP. @@ -1086,8 +1631,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.schema.ListSchemasResponse: @@ -1097,6 +1644,7 @@ def __call__( http_options = ( _BaseSchemaServiceRestTransport._BaseListSchemas._get_http_options() ) + request, metadata = self._interceptor.pre_list_schemas(request, metadata) transcoded_request = _BaseSchemaServiceRestTransport._BaseListSchemas._get_transcoded_request( http_options, request @@ -1109,6 +1657,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SchemaServiceClient.ListSchemas", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "ListSchemas", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SchemaServiceRestTransport._ListSchemas._get_response( self._host, @@ -1129,7 +1704,33 @@ def __call__( pb_resp = schema.ListSchemasResponse.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_list_schemas(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_list_schemas_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = schema.ListSchemasResponse.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SchemaServiceClient.list_schemas", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "ListSchemas", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _RollbackSchema( @@ -1167,7 +1768,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> schema.Schema: r"""Call the rollback schema method over HTTP. @@ -1177,8 +1778,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.schema.Schema: @@ -1188,6 +1791,7 @@ def __call__( http_options = ( _BaseSchemaServiceRestTransport._BaseRollbackSchema._get_http_options() ) + request, metadata = self._interceptor.pre_rollback_schema(request, metadata) transcoded_request = _BaseSchemaServiceRestTransport._BaseRollbackSchema._get_transcoded_request( http_options, request @@ -1202,6 +1806,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SchemaServiceClient.RollbackSchema", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "RollbackSchema", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SchemaServiceRestTransport._RollbackSchema._get_response( self._host, @@ -1223,7 +1854,33 @@ def __call__( pb_resp = schema.Schema.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_rollback_schema(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_rollback_schema_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = schema.Schema.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SchemaServiceClient.rollback_schema", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "RollbackSchema", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _ValidateMessage( @@ -1261,7 +1918,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> schema.ValidateMessageResponse: r"""Call the validate message method over HTTP. @@ -1271,8 +1928,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.schema.ValidateMessageResponse: @@ -1284,6 +1943,7 @@ def __call__( http_options = ( _BaseSchemaServiceRestTransport._BaseValidateMessage._get_http_options() ) + request, metadata = self._interceptor.pre_validate_message( request, metadata ) @@ -1300,6 +1960,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SchemaServiceClient.ValidateMessage", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "ValidateMessage", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SchemaServiceRestTransport._ValidateMessage._get_response( self._host, @@ -1321,7 +2008,33 @@ def __call__( pb_resp = schema.ValidateMessageResponse.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_validate_message(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_validate_message_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = schema.ValidateMessageResponse.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SchemaServiceClient.validate_message", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "ValidateMessage", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _ValidateSchema( @@ -1359,7 +2072,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> gp_schema.ValidateSchemaResponse: r"""Call the validate schema method over HTTP. @@ -1369,8 +2082,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.gp_schema.ValidateSchemaResponse: @@ -1382,6 +2097,7 @@ def __call__( http_options = ( _BaseSchemaServiceRestTransport._BaseValidateSchema._get_http_options() ) + request, metadata = self._interceptor.pre_validate_schema(request, metadata) transcoded_request = _BaseSchemaServiceRestTransport._BaseValidateSchema._get_transcoded_request( http_options, request @@ -1396,6 +2112,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SchemaServiceClient.ValidateSchema", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "ValidateSchema", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SchemaServiceRestTransport._ValidateSchema._get_response( self._host, @@ -1417,7 +2160,35 @@ def __call__( pb_resp = gp_schema.ValidateSchemaResponse.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_validate_schema(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_validate_schema_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = gp_schema.ValidateSchemaResponse.to_json( + response + ) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SchemaServiceClient.validate_schema", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "ValidateSchema", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp @property @@ -1536,7 +2307,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Call the get iam policy method over HTTP. @@ -1546,8 +2317,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: policy_pb2.Policy: Response from GetIamPolicy method. @@ -1556,6 +2329,7 @@ def __call__( http_options = ( _BaseSchemaServiceRestTransport._BaseGetIamPolicy._get_http_options() ) + request, metadata = self._interceptor.pre_get_iam_policy(request, metadata) transcoded_request = _BaseSchemaServiceRestTransport._BaseGetIamPolicy._get_transcoded_request( http_options, request @@ -1566,6 +2340,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SchemaServiceClient.GetIamPolicy", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "GetIamPolicy", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SchemaServiceRestTransport._GetIamPolicy._get_response( self._host, @@ -1585,6 +2386,27 @@ def __call__( resp = policy_pb2.Policy() resp = json_format.Parse(content, resp) resp = self._interceptor.post_get_iam_policy(resp) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = json_format.MessageToJson(resp) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SchemaServiceAsyncClient.GetIamPolicy", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "GetIamPolicy", + "httpResponse": http_response, + "metadata": http_response["headers"], + }, + ) return resp @property @@ -1626,7 +2448,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Call the set iam policy method over HTTP. @@ -1636,8 +2458,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: policy_pb2.Policy: Response from SetIamPolicy method. @@ -1646,6 +2470,7 @@ def __call__( http_options = ( _BaseSchemaServiceRestTransport._BaseSetIamPolicy._get_http_options() ) + request, metadata = self._interceptor.pre_set_iam_policy(request, metadata) transcoded_request = _BaseSchemaServiceRestTransport._BaseSetIamPolicy._get_transcoded_request( http_options, request @@ -1660,6 +2485,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SchemaServiceClient.SetIamPolicy", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "SetIamPolicy", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SchemaServiceRestTransport._SetIamPolicy._get_response( self._host, @@ -1680,6 +2532,27 @@ def __call__( resp = policy_pb2.Policy() resp = json_format.Parse(content, resp) resp = self._interceptor.post_set_iam_policy(resp) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = json_format.MessageToJson(resp) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SchemaServiceAsyncClient.SetIamPolicy", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "SetIamPolicy", + "httpResponse": http_response, + "metadata": http_response["headers"], + }, + ) return resp @property @@ -1721,7 +2594,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> iam_policy_pb2.TestIamPermissionsResponse: r"""Call the test iam permissions method over HTTP. @@ -1731,8 +2604,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: iam_policy_pb2.TestIamPermissionsResponse: Response from TestIamPermissions method. @@ -1741,6 +2616,7 @@ def __call__( http_options = ( _BaseSchemaServiceRestTransport._BaseTestIamPermissions._get_http_options() ) + request, metadata = self._interceptor.pre_test_iam_permissions( request, metadata ) @@ -1757,6 +2633,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SchemaServiceClient.TestIamPermissions", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "TestIamPermissions", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SchemaServiceRestTransport._TestIamPermissions._get_response( self._host, @@ -1777,6 +2680,27 @@ def __call__( resp = iam_policy_pb2.TestIamPermissionsResponse() resp = json_format.Parse(content, resp) resp = self._interceptor.post_test_iam_permissions(resp) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = json_format.MessageToJson(resp) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SchemaServiceAsyncClient.TestIamPermissions", + extra={ + "serviceName": "google.pubsub.v1.SchemaService", + "rpcName": "TestIamPermissions", + "httpResponse": http_response, + "metadata": http_response["headers"], + }, + ) return resp @property diff --git a/google/pubsub_v1/services/schema_service/transports/rest_base.py b/google/pubsub_v1/services/schema_service/transports/rest_base.py index a97e454d4..94312eba7 100644 --- a/google/pubsub_v1/services/schema_service/transports/rest_base.py +++ b/google/pubsub_v1/services/schema_service/transports/rest_base.py @@ -26,8 +26,6 @@ from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union -from google.iam.v1 import iam_policy_pb2 # type: ignore -from google.iam.v1 import policy_pb2 # type: ignore from google.protobuf import empty_pb2 # type: ignore from google.pubsub_v1.types import schema from google.pubsub_v1.types import schema as gp_schema diff --git a/google/pubsub_v1/services/subscriber/async_client.py b/google/pubsub_v1/services/subscriber/async_client.py index 7f40480ef..9fc50fd31 100644 --- a/google/pubsub_v1/services/subscriber/async_client.py +++ b/google/pubsub_v1/services/subscriber/async_client.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import logging as std_logging from collections import OrderedDict import re from typing import ( @@ -58,6 +59,15 @@ from .transports.grpc_asyncio import SubscriberGrpcAsyncIOTransport from .client import SubscriberClient +try: + from google.api_core import client_logging # type: ignore + + CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER +except ImportError: # pragma: NO COVER + CLIENT_LOGGING_SUPPORTED = False + +_LOGGER = std_logging.getLogger(__name__) + class SubscriberAsyncClient: """The service that an application uses to manipulate subscriptions and @@ -265,6 +275,28 @@ def __init__( client_info=client_info, ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + std_logging.DEBUG + ): # pragma: NO COVER + _LOGGER.debug( + "Created client `google.pubsub_v1.SubscriberAsyncClient`.", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "universeDomain": getattr( + self._client._transport._credentials, "universe_domain", "" + ), + "credentialsType": f"{type(self._client._transport._credentials).__module__}.{type(self._client._transport._credentials).__qualname__}", + "credentialsInfo": getattr( + self.transport._credentials, "get_cred_info", lambda: None + )(), + } + if hasattr(self._client._transport, "_credentials") + else { + "serviceName": "google.pubsub.v1.Subscriber", + "credentialsType": None, + }, + ) + async def create_subscription( self, request: Optional[Union[pubsub.Subscription, dict]] = None, @@ -275,7 +307,7 @@ async def create_subscription( ack_deadline_seconds: Optional[int] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Subscription: r"""Creates a subscription to a given topic. See the [resource name rules] @@ -388,8 +420,10 @@ async def sample_create_subscription(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Subscription: @@ -402,7 +436,10 @@ async def sample_create_subscription(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name, topic, push_config, ack_deadline_seconds]) + flattened_params = [name, topic, push_config, ack_deadline_seconds] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -458,7 +495,7 @@ async def get_subscription( subscription: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Subscription: r"""Gets the configuration details of a subscription. @@ -502,8 +539,10 @@ async def sample_get_subscription(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Subscription: @@ -516,7 +555,10 @@ async def sample_get_subscription(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([subscription]) + flattened_params = [subscription] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -569,7 +611,7 @@ async def update_subscription( update_mask: Optional[field_mask_pb2.FieldMask] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Subscription: r"""Updates an existing subscription by updating the fields specified in the update mask. Note that certain @@ -628,8 +670,10 @@ async def sample_update_subscription(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Subscription: @@ -642,7 +686,10 @@ async def sample_update_subscription(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([subscription, update_mask]) + flattened_params = [subscription, update_mask] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -696,7 +743,7 @@ async def list_subscriptions( project: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pagers.ListSubscriptionsAsyncPager: r"""Lists matching subscriptions. @@ -740,8 +787,10 @@ async def sample_list_subscriptions(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.services.subscriber.pagers.ListSubscriptionsAsyncPager: @@ -754,7 +803,10 @@ async def sample_list_subscriptions(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([project]) + flattened_params = [project] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -815,7 +867,7 @@ async def delete_subscription( subscription: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> None: r"""Deletes an existing subscription. All messages retained in the subscription are immediately dropped. Calls to ``Pull`` after @@ -861,13 +913,18 @@ async def sample_delete_subscription(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([subscription]) + flattened_params = [subscription] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -918,7 +975,7 @@ async def modify_ack_deadline( ack_deadline_seconds: Optional[int] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> None: r"""Modifies the ack deadline for a specific message. This method is useful to indicate that more time is needed to process a message @@ -987,13 +1044,18 @@ async def sample_modify_ack_deadline(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([subscription, ack_ids, ack_deadline_seconds]) + flattened_params = [subscription, ack_ids, ack_deadline_seconds] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1047,7 +1109,7 @@ async def acknowledge( ack_ids: Optional[MutableSequence[str]] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> None: r"""Acknowledges the messages associated with the ``ack_ids`` in the ``AcknowledgeRequest``. The Pub/Sub system can remove the @@ -1104,13 +1166,18 @@ async def sample_acknowledge(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([subscription, ack_ids]) + flattened_params = [subscription, ack_ids] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1163,7 +1230,7 @@ async def pull( max_messages: Optional[int] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.PullResponse: r"""Pulls messages from the server. @@ -1232,8 +1299,10 @@ async def sample_pull(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.PullResponse: @@ -1242,7 +1311,10 @@ async def sample_pull(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([subscription, return_immediately, max_messages]) + flattened_params = [subscription, return_immediately, max_messages] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1301,7 +1373,7 @@ def streaming_pull( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> Awaitable[AsyncIterable[pubsub.StreamingPullResponse]]: r"""Establishes a stream with the server, which sends messages down to the client. The client streams acknowledgements and ack @@ -1359,8 +1431,10 @@ def request_generator(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: AsyncIterable[google.pubsub_v1.types.StreamingPullResponse]: @@ -1397,7 +1471,7 @@ async def modify_push_config( push_config: Optional[pubsub.PushConfig] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> None: r"""Modifies the ``PushConfig`` for a specified subscription. @@ -1456,13 +1530,18 @@ async def sample_modify_push_config(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([subscription, push_config]) + flattened_params = [subscription, push_config] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1513,7 +1592,7 @@ async def get_snapshot( snapshot: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Snapshot: r"""Gets the configuration details of a snapshot. Snapshots are used in @@ -1561,8 +1640,10 @@ async def sample_get_snapshot(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Snapshot: @@ -1577,7 +1658,10 @@ async def sample_get_snapshot(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([snapshot]) + flattened_params = [snapshot] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1627,7 +1711,7 @@ async def list_snapshots( project: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pagers.ListSnapshotsAsyncPager: r"""Lists the existing snapshots. Snapshots are used in `Seek `__ @@ -1675,8 +1759,10 @@ async def sample_list_snapshots(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.services.subscriber.pagers.ListSnapshotsAsyncPager: @@ -1689,7 +1775,10 @@ async def sample_list_snapshots(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([project]) + flattened_params = [project] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1751,7 +1840,7 @@ async def create_snapshot( subscription: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Snapshot: r"""Creates a snapshot from the requested subscription. Snapshots are used in @@ -1834,8 +1923,10 @@ async def sample_create_snapshot(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Snapshot: @@ -1850,7 +1941,10 @@ async def sample_create_snapshot(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name, subscription]) + flattened_params = [name, subscription] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1903,7 +1997,7 @@ async def update_snapshot( update_mask: Optional[field_mask_pb2.FieldMask] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Snapshot: r"""Updates an existing snapshot by updating the fields specified in the update mask. Snapshots are used in @@ -1959,8 +2053,10 @@ async def sample_update_snapshot(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Snapshot: @@ -1975,7 +2071,10 @@ async def sample_update_snapshot(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([snapshot, update_mask]) + flattened_params = [snapshot, update_mask] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -2029,7 +2128,7 @@ async def delete_snapshot( snapshot: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> None: r"""Removes an existing snapshot. Snapshots are used in [Seek] (https://cloud.google.com/pubsub/docs/replay-overview) @@ -2078,13 +2177,18 @@ async def sample_delete_snapshot(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([snapshot]) + flattened_params = [snapshot] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -2130,7 +2234,7 @@ async def seek( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.SeekResponse: r"""Seeks an existing subscription to a point in time or to a given snapshot, whichever is provided in the request. Snapshots are @@ -2174,8 +2278,10 @@ async def sample_seek(): retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.SeekResponse: @@ -2219,21 +2325,23 @@ async def set_iam_policy( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Sets the IAM access control policy on the specified function. Replaces any existing policy. Args: - request (:class:`~.policy_pb2.SetIamPolicyRequest`): + request (:class:`~.iam_policy_pb2.SetIamPolicyRequest`): The request object. Request message for `SetIamPolicy` method. retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.policy_pb2.Policy: Defines an Identity and Access Management (IAM) policy. @@ -2253,6 +2361,7 @@ async def set_iam_policy( **JSON Example** :: + { "bindings": [ { @@ -2337,22 +2446,24 @@ async def get_iam_policy( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Gets the IAM access control policy for a function. - Returns an empty policy if the function exists and does - not have a policy set. + Returns an empty policy if the function exists and does not have a + policy set. Args: request (:class:`~.iam_policy_pb2.GetIamPolicyRequest`): The request object. Request message for `GetIamPolicy` method. - retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, - should be retried. + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if + any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.policy_pb2.Policy: Defines an Identity and Access Management (IAM) policy. @@ -2457,25 +2568,27 @@ async def test_iam_permissions( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> iam_policy_pb2.TestIamPermissionsResponse: - r"""Tests the specified permissions against the IAM access control + r"""Tests the specified IAM permissions against the IAM access control policy for a function. - If the function does not exist, this will - return an empty set of permissions, not a NOT_FOUND error. + If the function does not exist, this will return an empty set + of permissions, not a NOT_FOUND error. Args: request (:class:`~.iam_policy_pb2.TestIamPermissionsRequest`): The request object. Request message for `TestIamPermissions` method. - retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, - should be retried. + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, + if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: - ~iam_policy_pb2.PolicyTestIamPermissionsResponse: + ~.iam_policy_pb2.TestIamPermissionsResponse: Response message for ``TestIamPermissions`` method. """ # Create or coerce a protobuf request object. diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index d601b0f0d..29c7be980 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -14,6 +14,9 @@ # limitations under the License. # from collections import OrderedDict +from http import HTTPStatus +import json +import logging as std_logging import functools import os import re @@ -52,6 +55,15 @@ except AttributeError: # pragma: NO COVER OptionalRetry = Union[retries.Retry, object, None] # type: ignore +try: + from google.api_core import client_logging # type: ignore + + CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER +except ImportError: # pragma: NO COVER + CLIENT_LOGGING_SUPPORTED = False + +_LOGGER = std_logging.getLogger(__name__) + from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore from google.protobuf import duration_pb2 # type: ignore @@ -528,6 +540,33 @@ def _validate_universe_domain(self): # NOTE (b/349488459): universe validation is disabled until further notice. return True + def _add_cred_info_for_auth_errors( + self, error: core_exceptions.GoogleAPICallError + ) -> None: + """Adds credential info string to error details for 401/403/404 errors. + + Args: + error (google.api_core.exceptions.GoogleAPICallError): The error to add the cred info. + """ + if error.code not in [ + HTTPStatus.UNAUTHORIZED, + HTTPStatus.FORBIDDEN, + HTTPStatus.NOT_FOUND, + ]: + return + + cred = self._transport._credentials + + # get_cred_info is only available in google-auth>=2.35.0 + if not hasattr(cred, "get_cred_info"): + return + + # ignore the type check since pypy test fails when get_cred_info + # is not available + cred_info = cred.get_cred_info() # type: ignore + if cred_info and hasattr(error._details, "append"): + error._details.append(json.dumps(cred_info)) + @property def api_endpoint(self): """Return the API endpoint used by the client instance. @@ -632,6 +671,10 @@ def __init__( # Initialize the universe domain validation. self._is_universe_domain_valid = False + if CLIENT_LOGGING_SUPPORTED: # pragma: NO COVER + # Setup logging. + client_logging.initialize_logging() + api_key_value = getattr(self._client_options, "api_key", None) if api_key_value and credentials: raise ValueError( @@ -703,6 +746,29 @@ def __init__( api_audience=self._client_options.api_audience, ) + if "async" not in str(self._transport): + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + std_logging.DEBUG + ): # pragma: NO COVER + _LOGGER.debug( + "Created client `google.pubsub_v1.SubscriberClient`.", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "universeDomain": getattr( + self._transport._credentials, "universe_domain", "" + ), + "credentialsType": f"{type(self._transport._credentials).__module__}.{type(self._transport._credentials).__qualname__}", + "credentialsInfo": getattr( + self.transport._credentials, "get_cred_info", lambda: None + )(), + } + if hasattr(self._transport, "_credentials") + else { + "serviceName": "google.pubsub.v1.Subscriber", + "credentialsType": None, + }, + ) + def create_subscription( self, request: Optional[Union[pubsub.Subscription, dict]] = None, @@ -713,7 +779,7 @@ def create_subscription( ack_deadline_seconds: Optional[int] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Subscription: r"""Creates a subscription to a given topic. See the [resource name rules] @@ -826,8 +892,10 @@ def sample_create_subscription(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Subscription: @@ -840,7 +908,10 @@ def sample_create_subscription(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name, topic, push_config, ack_deadline_seconds]) + flattened_params = [name, topic, push_config, ack_deadline_seconds] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -893,7 +964,7 @@ def get_subscription( subscription: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Subscription: r"""Gets the configuration details of a subscription. @@ -937,8 +1008,10 @@ def sample_get_subscription(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Subscription: @@ -951,7 +1024,10 @@ def sample_get_subscription(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([subscription]) + flattened_params = [subscription] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1001,7 +1077,7 @@ def update_subscription( update_mask: Optional[field_mask_pb2.FieldMask] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Subscription: r"""Updates an existing subscription by updating the fields specified in the update mask. Note that certain @@ -1060,8 +1136,10 @@ def sample_update_subscription(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Subscription: @@ -1074,7 +1152,10 @@ def sample_update_subscription(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([subscription, update_mask]) + flattened_params = [subscription, update_mask] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1125,7 +1206,7 @@ def list_subscriptions( project: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pagers.ListSubscriptionsPager: r"""Lists matching subscriptions. @@ -1169,8 +1250,10 @@ def sample_list_subscriptions(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.services.subscriber.pagers.ListSubscriptionsPager: @@ -1183,7 +1266,10 @@ def sample_list_subscriptions(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([project]) + flattened_params = [project] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1241,7 +1327,7 @@ def delete_subscription( subscription: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> None: r"""Deletes an existing subscription. All messages retained in the subscription are immediately dropped. Calls to ``Pull`` after @@ -1287,13 +1373,18 @@ def sample_delete_subscription(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([subscription]) + flattened_params = [subscription] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1341,7 +1432,7 @@ def modify_ack_deadline( ack_deadline_seconds: Optional[int] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> None: r"""Modifies the ack deadline for a specific message. This method is useful to indicate that more time is needed to process a message @@ -1410,13 +1501,18 @@ def sample_modify_ack_deadline(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([subscription, ack_ids, ack_deadline_seconds]) + flattened_params = [subscription, ack_ids, ack_deadline_seconds] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1467,7 +1563,7 @@ def acknowledge( ack_ids: Optional[MutableSequence[str]] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> None: r"""Acknowledges the messages associated with the ``ack_ids`` in the ``AcknowledgeRequest``. The Pub/Sub system can remove the @@ -1524,13 +1620,18 @@ def sample_acknowledge(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([subscription, ack_ids]) + flattened_params = [subscription, ack_ids] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1580,7 +1681,7 @@ def pull( max_messages: Optional[int] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.PullResponse: r"""Pulls messages from the server. @@ -1649,8 +1750,10 @@ def sample_pull(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.PullResponse: @@ -1659,7 +1762,10 @@ def sample_pull(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([subscription, return_immediately, max_messages]) + flattened_params = [subscription, return_immediately, max_messages] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1717,7 +1823,7 @@ def streaming_pull( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> Iterable[pubsub.StreamingPullResponse]: r"""Establishes a stream with the server, which sends messages down to the client. The client streams acknowledgements and ack @@ -1775,8 +1881,10 @@ def request_generator(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: Iterable[google.pubsub_v1.types.StreamingPullResponse]: @@ -1816,7 +1924,7 @@ def modify_push_config( push_config: Optional[pubsub.PushConfig] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> None: r"""Modifies the ``PushConfig`` for a specified subscription. @@ -1875,13 +1983,18 @@ def sample_modify_push_config(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([subscription, push_config]) + flattened_params = [subscription, push_config] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -1929,7 +2042,7 @@ def get_snapshot( snapshot: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Snapshot: r"""Gets the configuration details of a snapshot. Snapshots are used in @@ -1977,8 +2090,10 @@ def sample_get_snapshot(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Snapshot: @@ -1993,7 +2108,10 @@ def sample_get_snapshot(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([snapshot]) + flattened_params = [snapshot] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -2040,7 +2158,7 @@ def list_snapshots( project: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pagers.ListSnapshotsPager: r"""Lists the existing snapshots. Snapshots are used in `Seek `__ @@ -2088,8 +2206,10 @@ def sample_list_snapshots(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.services.subscriber.pagers.ListSnapshotsPager: @@ -2102,7 +2222,10 @@ def sample_list_snapshots(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([project]) + flattened_params = [project] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -2161,7 +2284,7 @@ def create_snapshot( subscription: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Snapshot: r"""Creates a snapshot from the requested subscription. Snapshots are used in @@ -2244,8 +2367,10 @@ def sample_create_snapshot(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Snapshot: @@ -2260,7 +2385,10 @@ def sample_create_snapshot(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([name, subscription]) + flattened_params = [name, subscription] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -2310,7 +2438,7 @@ def update_snapshot( update_mask: Optional[field_mask_pb2.FieldMask] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Snapshot: r"""Updates an existing snapshot by updating the fields specified in the update mask. Snapshots are used in @@ -2366,8 +2494,10 @@ def sample_update_snapshot(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.Snapshot: @@ -2382,7 +2512,10 @@ def sample_update_snapshot(): # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([snapshot, update_mask]) + flattened_params = [snapshot, update_mask] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -2433,7 +2566,7 @@ def delete_snapshot( snapshot: Optional[str] = None, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> None: r"""Removes an existing snapshot. Snapshots are used in [Seek] (https://cloud.google.com/pubsub/docs/replay-overview) @@ -2482,13 +2615,18 @@ def sample_delete_snapshot(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ # Create or coerce a protobuf request object. # - Quick check: If we got a request object, we should *not* have # gotten any keyword arguments that map to the request. - has_flattened_params = any([snapshot]) + flattened_params = [snapshot] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " @@ -2531,7 +2669,7 @@ def seek( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.SeekResponse: r"""Seeks an existing subscription to a point in time or to a given snapshot, whichever is provided in the request. Snapshots are @@ -2575,8 +2713,10 @@ def sample_seek(): retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: google.pubsub_v1.types.SeekResponse: @@ -2633,7 +2773,7 @@ def set_iam_policy( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Sets the IAM access control policy on the specified function. @@ -2646,8 +2786,10 @@ def set_iam_policy( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.policy_pb2.Policy: Defines an Identity and Access Management (IAM) policy. @@ -2724,11 +2866,7 @@ def set_iam_policy( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method.wrap_method( - self._transport.set_iam_policy, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._transport._wrapped_methods[self._transport.set_iam_policy] # Certain fields should be provided within the metadata header; # add these here. @@ -2739,16 +2877,20 @@ def set_iam_policy( # Validate the universe domain. self._validate_universe_domain() - # Send the request. - response = rpc( - request, - retry=retry, - timeout=timeout, - metadata=metadata, - ) + try: + # Send the request. + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) - # Done; return the response. - return response + # Done; return the response. + return response + except core_exceptions.GoogleAPICallError as e: + self._add_cred_info_for_auth_errors(e) + raise e def get_iam_policy( self, @@ -2756,7 +2898,7 @@ def get_iam_policy( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Gets the IAM access control policy for a function. @@ -2770,8 +2912,10 @@ def get_iam_policy( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.policy_pb2.Policy: Defines an Identity and Access Management (IAM) policy. @@ -2848,11 +2992,7 @@ def get_iam_policy( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method.wrap_method( - self._transport.get_iam_policy, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._transport._wrapped_methods[self._transport.get_iam_policy] # Certain fields should be provided within the metadata header; # add these here. @@ -2863,16 +3003,20 @@ def get_iam_policy( # Validate the universe domain. self._validate_universe_domain() - # Send the request. - response = rpc( - request, - retry=retry, - timeout=timeout, - metadata=metadata, - ) + try: + # Send the request. + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) - # Done; return the response. - return response + # Done; return the response. + return response + except core_exceptions.GoogleAPICallError as e: + self._add_cred_info_for_auth_errors(e) + raise e def test_iam_permissions( self, @@ -2880,7 +3024,7 @@ def test_iam_permissions( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> iam_policy_pb2.TestIamPermissionsResponse: r"""Tests the specified IAM permissions against the IAM access control policy for a function. @@ -2895,8 +3039,10 @@ def test_iam_permissions( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.iam_policy_pb2.TestIamPermissionsResponse: Response message for ``TestIamPermissions`` method. @@ -2910,11 +3056,7 @@ def test_iam_permissions( # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. - rpc = gapic_v1.method.wrap_method( - self._transport.test_iam_permissions, - default_timeout=None, - client_info=DEFAULT_CLIENT_INFO, - ) + rpc = self._transport._wrapped_methods[self._transport.test_iam_permissions] # Certain fields should be provided within the metadata header; # add these here. @@ -2925,16 +3067,20 @@ def test_iam_permissions( # Validate the universe domain. self._validate_universe_domain() - # Send the request. - response = rpc( - request, - retry=retry, - timeout=timeout, - metadata=metadata, - ) + try: + # Send the request. + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) - # Done; return the response. - return response + # Done; return the response. + return response + except core_exceptions.GoogleAPICallError as e: + self._add_cred_info_for_auth_errors(e) + raise e DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( diff --git a/google/pubsub_v1/services/subscriber/pagers.py b/google/pubsub_v1/services/subscriber/pagers.py index c09c42027..26adcdd63 100644 --- a/google/pubsub_v1/services/subscriber/pagers.py +++ b/google/pubsub_v1/services/subscriber/pagers.py @@ -66,7 +66,7 @@ def __init__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = () + metadata: Sequence[Tuple[str, Union[str, bytes]]] = () ): """Instantiate the pager. @@ -80,8 +80,10 @@ def __init__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ self._method = method self._request = pubsub.ListSubscriptionsRequest(request) @@ -140,7 +142,7 @@ def __init__( *, retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = () + metadata: Sequence[Tuple[str, Union[str, bytes]]] = () ): """Instantiates the pager. @@ -154,8 +156,10 @@ def __init__( retry (google.api_core.retry.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ self._method = method self._request = pubsub.ListSubscriptionsRequest(request) @@ -218,7 +222,7 @@ def __init__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = () + metadata: Sequence[Tuple[str, Union[str, bytes]]] = () ): """Instantiate the pager. @@ -232,8 +236,10 @@ def __init__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ self._method = method self._request = pubsub.ListSnapshotsRequest(request) @@ -292,7 +298,7 @@ def __init__( *, retry: OptionalAsyncRetry = gapic_v1.method.DEFAULT, timeout: Union[float, object] = gapic_v1.method.DEFAULT, - metadata: Sequence[Tuple[str, str]] = () + metadata: Sequence[Tuple[str, Union[str, bytes]]] = () ): """Instantiates the pager. @@ -306,8 +312,10 @@ def __init__( retry (google.api_core.retry.AsyncRetry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ self._method = method self._request = pubsub.ListSnapshotsRequest(request) diff --git a/google/pubsub_v1/services/subscriber/transports/grpc.py b/google/pubsub_v1/services/subscriber/transports/grpc.py index e706190ce..ff59214ca 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc.py @@ -13,6 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import json +import logging as std_logging +import pickle import warnings from typing import Callable, Dict, Optional, Sequence, Tuple, Union @@ -21,8 +24,11 @@ import google.auth # type: ignore from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore +from google.protobuf.json_format import MessageToJson +import google.protobuf.message import grpc # type: ignore +import proto # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -30,6 +36,81 @@ from google.pubsub_v1.types import pubsub from .base import SubscriberTransport, DEFAULT_CLIENT_INFO +try: + from google.api_core import client_logging # type: ignore + + CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER +except ImportError: # pragma: NO COVER + CLIENT_LOGGING_SUPPORTED = False + +_LOGGER = std_logging.getLogger(__name__) + + +class _LoggingClientInterceptor(grpc.UnaryUnaryClientInterceptor): # pragma: NO COVER + def intercept_unary_unary(self, continuation, client_call_details, request): + logging_enabled = CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + std_logging.DEBUG + ) + if logging_enabled: # pragma: NO COVER + request_metadata = client_call_details.metadata + if isinstance(request, proto.Message): + request_payload = type(request).to_json(request) + elif isinstance(request, google.protobuf.message.Message): + request_payload = MessageToJson(request) + else: + request_payload = f"{type(request).__name__}: {pickle.dumps(request)}" + + request_metadata = { + key: value.decode("utf-8") if isinstance(value, bytes) else value + for key, value in request_metadata + } + grpc_request = { + "payload": request_payload, + "requestMethod": "grpc", + "metadata": dict(request_metadata), + } + _LOGGER.debug( + f"Sending request for {client_call_details.method}", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": client_call_details.method, + "request": grpc_request, + "metadata": grpc_request["metadata"], + }, + ) + + response = continuation(client_call_details, request) + if logging_enabled: # pragma: NO COVER + response_metadata = response.trailing_metadata() + # Convert gRPC metadata `` to list of tuples + metadata = ( + dict([(k, str(v)) for k, v in response_metadata]) + if response_metadata + else None + ) + result = response.result() + if isinstance(result, proto.Message): + response_payload = type(result).to_json(result) + elif isinstance(result, google.protobuf.message.Message): + response_payload = MessageToJson(result) + else: + response_payload = f"{type(result).__name__}: {pickle.dumps(result)}" + grpc_response = { + "payload": response_payload, + "metadata": metadata, + "status": "OK", + } + _LOGGER.debug( + f"Received response for {client_call_details.method}.", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": client_call_details.method, + "response": grpc_response, + "metadata": grpc_response["metadata"], + }, + ) + return response + class SubscriberGrpcTransport(SubscriberTransport): """gRPC backend transport for Subscriber. @@ -188,7 +269,12 @@ def __init__( ], ) - # Wrap messages. This must be done after self._grpc_channel exists + self._interceptor = _LoggingClientInterceptor() + self._logged_channel = grpc.intercept_channel( + self._grpc_channel, self._interceptor + ) + + # Wrap messages. This must be done after self._logged_channel exists self._prep_wrapped_messages(client_info) @classmethod @@ -274,7 +360,7 @@ def create_subscription( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "create_subscription" not in self._stubs: - self._stubs["create_subscription"] = self.grpc_channel.unary_unary( + self._stubs["create_subscription"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/CreateSubscription", request_serializer=pubsub.Subscription.serialize, response_deserializer=pubsub.Subscription.deserialize, @@ -300,7 +386,7 @@ def get_subscription( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "get_subscription" not in self._stubs: - self._stubs["get_subscription"] = self.grpc_channel.unary_unary( + self._stubs["get_subscription"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/GetSubscription", request_serializer=pubsub.GetSubscriptionRequest.serialize, response_deserializer=pubsub.Subscription.deserialize, @@ -329,7 +415,7 @@ def update_subscription( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "update_subscription" not in self._stubs: - self._stubs["update_subscription"] = self.grpc_channel.unary_unary( + self._stubs["update_subscription"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/UpdateSubscription", request_serializer=pubsub.UpdateSubscriptionRequest.serialize, response_deserializer=pubsub.Subscription.deserialize, @@ -355,7 +441,7 @@ def list_subscriptions( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "list_subscriptions" not in self._stubs: - self._stubs["list_subscriptions"] = self.grpc_channel.unary_unary( + self._stubs["list_subscriptions"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/ListSubscriptions", request_serializer=pubsub.ListSubscriptionsRequest.serialize, response_deserializer=pubsub.ListSubscriptionsResponse.deserialize, @@ -386,7 +472,7 @@ def delete_subscription( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "delete_subscription" not in self._stubs: - self._stubs["delete_subscription"] = self.grpc_channel.unary_unary( + self._stubs["delete_subscription"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/DeleteSubscription", request_serializer=pubsub.DeleteSubscriptionRequest.serialize, response_deserializer=empty_pb2.Empty.FromString, @@ -417,7 +503,7 @@ def modify_ack_deadline( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "modify_ack_deadline" not in self._stubs: - self._stubs["modify_ack_deadline"] = self.grpc_channel.unary_unary( + self._stubs["modify_ack_deadline"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/ModifyAckDeadline", request_serializer=pubsub.ModifyAckDeadlineRequest.serialize, response_deserializer=empty_pb2.Empty.FromString, @@ -448,7 +534,7 @@ def acknowledge(self) -> Callable[[pubsub.AcknowledgeRequest], empty_pb2.Empty]: # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "acknowledge" not in self._stubs: - self._stubs["acknowledge"] = self.grpc_channel.unary_unary( + self._stubs["acknowledge"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/Acknowledge", request_serializer=pubsub.AcknowledgeRequest.serialize, response_deserializer=empty_pb2.Empty.FromString, @@ -472,7 +558,7 @@ def pull(self) -> Callable[[pubsub.PullRequest], pubsub.PullResponse]: # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "pull" not in self._stubs: - self._stubs["pull"] = self.grpc_channel.unary_unary( + self._stubs["pull"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/Pull", request_serializer=pubsub.PullRequest.serialize, response_deserializer=pubsub.PullResponse.deserialize, @@ -505,7 +591,7 @@ def streaming_pull( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "streaming_pull" not in self._stubs: - self._stubs["streaming_pull"] = self.grpc_channel.stream_stream( + self._stubs["streaming_pull"] = self._logged_channel.stream_stream( "/google.pubsub.v1.Subscriber/StreamingPull", request_serializer=pubsub.StreamingPullRequest.serialize, response_deserializer=pubsub.StreamingPullResponse.deserialize, @@ -537,7 +623,7 @@ def modify_push_config( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "modify_push_config" not in self._stubs: - self._stubs["modify_push_config"] = self.grpc_channel.unary_unary( + self._stubs["modify_push_config"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/ModifyPushConfig", request_serializer=pubsub.ModifyPushConfigRequest.serialize, response_deserializer=empty_pb2.Empty.FromString, @@ -566,7 +652,7 @@ def get_snapshot(self) -> Callable[[pubsub.GetSnapshotRequest], pubsub.Snapshot] # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "get_snapshot" not in self._stubs: - self._stubs["get_snapshot"] = self.grpc_channel.unary_unary( + self._stubs["get_snapshot"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/GetSnapshot", request_serializer=pubsub.GetSnapshotRequest.serialize, response_deserializer=pubsub.Snapshot.deserialize, @@ -596,7 +682,7 @@ def list_snapshots( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "list_snapshots" not in self._stubs: - self._stubs["list_snapshots"] = self.grpc_channel.unary_unary( + self._stubs["list_snapshots"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/ListSnapshots", request_serializer=pubsub.ListSnapshotsRequest.serialize, response_deserializer=pubsub.ListSnapshotsResponse.deserialize, @@ -640,7 +726,7 @@ def create_snapshot( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "create_snapshot" not in self._stubs: - self._stubs["create_snapshot"] = self.grpc_channel.unary_unary( + self._stubs["create_snapshot"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/CreateSnapshot", request_serializer=pubsub.CreateSnapshotRequest.serialize, response_deserializer=pubsub.Snapshot.deserialize, @@ -671,7 +757,7 @@ def update_snapshot( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "update_snapshot" not in self._stubs: - self._stubs["update_snapshot"] = self.grpc_channel.unary_unary( + self._stubs["update_snapshot"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/UpdateSnapshot", request_serializer=pubsub.UpdateSnapshotRequest.serialize, response_deserializer=pubsub.Snapshot.deserialize, @@ -706,7 +792,7 @@ def delete_snapshot( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "delete_snapshot" not in self._stubs: - self._stubs["delete_snapshot"] = self.grpc_channel.unary_unary( + self._stubs["delete_snapshot"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/DeleteSnapshot", request_serializer=pubsub.DeleteSnapshotRequest.serialize, response_deserializer=empty_pb2.Empty.FromString, @@ -738,13 +824,16 @@ def seek(self) -> Callable[[pubsub.SeekRequest], pubsub.SeekResponse]: # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "seek" not in self._stubs: - self._stubs["seek"] = self.grpc_channel.unary_unary( + self._stubs["seek"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/Seek", request_serializer=pubsub.SeekRequest.serialize, response_deserializer=pubsub.SeekResponse.deserialize, ) return self._stubs["seek"] + def close(self): + self._logged_channel.close() + @property def set_iam_policy( self, @@ -763,7 +852,7 @@ def set_iam_policy( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "set_iam_policy" not in self._stubs: - self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( + self._stubs["set_iam_policy"] = self._logged_channel.unary_unary( "/google.iam.v1.IAMPolicy/SetIamPolicy", request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, response_deserializer=policy_pb2.Policy.FromString, @@ -789,7 +878,7 @@ def get_iam_policy( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "get_iam_policy" not in self._stubs: - self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( + self._stubs["get_iam_policy"] = self._logged_channel.unary_unary( "/google.iam.v1.IAMPolicy/GetIamPolicy", request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, response_deserializer=policy_pb2.Policy.FromString, @@ -818,16 +907,13 @@ def test_iam_permissions( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "test_iam_permissions" not in self._stubs: - self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( + self._stubs["test_iam_permissions"] = self._logged_channel.unary_unary( "/google.iam.v1.IAMPolicy/TestIamPermissions", request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, ) return self._stubs["test_iam_permissions"] - def close(self): - self.grpc_channel.close() - @property def kind(self) -> str: return "grpc" diff --git a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py index de9606858..08eaf9665 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py @@ -14,6 +14,9 @@ # limitations under the License. # import inspect +import json +import pickle +import logging as std_logging import warnings from typing import Awaitable, Callable, Dict, Optional, Sequence, Tuple, Union @@ -23,8 +26,11 @@ from google.api_core import retry_async as retries from google.auth import credentials as ga_credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore +from google.protobuf.json_format import MessageToJson +import google.protobuf.message import grpc # type: ignore +import proto # type: ignore from grpc.experimental import aio # type: ignore from google.iam.v1 import iam_policy_pb2 # type: ignore @@ -34,6 +40,82 @@ from .base import SubscriberTransport, DEFAULT_CLIENT_INFO from .grpc import SubscriberGrpcTransport +try: + from google.api_core import client_logging # type: ignore + + CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER +except ImportError: # pragma: NO COVER + CLIENT_LOGGING_SUPPORTED = False + +_LOGGER = std_logging.getLogger(__name__) + + +class _LoggingClientAIOInterceptor( + grpc.aio.UnaryUnaryClientInterceptor +): # pragma: NO COVER + async def intercept_unary_unary(self, continuation, client_call_details, request): + logging_enabled = CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + std_logging.DEBUG + ) + if logging_enabled: # pragma: NO COVER + request_metadata = client_call_details.metadata + if isinstance(request, proto.Message): + request_payload = type(request).to_json(request) + elif isinstance(request, google.protobuf.message.Message): + request_payload = MessageToJson(request) + else: + request_payload = f"{type(request).__name__}: {pickle.dumps(request)}" + + request_metadata = { + key: value.decode("utf-8") if isinstance(value, bytes) else value + for key, value in request_metadata + } + grpc_request = { + "payload": request_payload, + "requestMethod": "grpc", + "metadata": dict(request_metadata), + } + _LOGGER.debug( + f"Sending request for {client_call_details.method}", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": str(client_call_details.method), + "request": grpc_request, + "metadata": grpc_request["metadata"], + }, + ) + response = await continuation(client_call_details, request) + if logging_enabled: # pragma: NO COVER + response_metadata = await response.trailing_metadata() + # Convert gRPC metadata `` to list of tuples + metadata = ( + dict([(k, str(v)) for k, v in response_metadata]) + if response_metadata + else None + ) + result = await response + if isinstance(result, proto.Message): + response_payload = type(result).to_json(result) + elif isinstance(result, google.protobuf.message.Message): + response_payload = MessageToJson(result) + else: + response_payload = f"{type(result).__name__}: {pickle.dumps(result)}" + grpc_response = { + "payload": response_payload, + "metadata": metadata, + "status": "OK", + } + _LOGGER.debug( + f"Received response to rpc {client_call_details.method}.", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": str(client_call_details.method), + "response": grpc_response, + "metadata": grpc_response["metadata"], + }, + ) + return response + class SubscriberGrpcAsyncIOTransport(SubscriberTransport): """gRPC AsyncIO backend transport for Subscriber. @@ -235,10 +317,13 @@ def __init__( ], ) - # Wrap messages. This must be done after self._grpc_channel exists + self._interceptor = _LoggingClientAIOInterceptor() + self._grpc_channel._unary_unary_interceptors.append(self._interceptor) + self._logged_channel = self._grpc_channel self._wrap_with_kind = ( "kind" in inspect.signature(gapic_v1.method_async.wrap_method).parameters ) + # Wrap messages. This must be done after self._logged_channel exists self._prep_wrapped_messages(client_info) @property @@ -282,7 +367,7 @@ def create_subscription( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "create_subscription" not in self._stubs: - self._stubs["create_subscription"] = self.grpc_channel.unary_unary( + self._stubs["create_subscription"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/CreateSubscription", request_serializer=pubsub.Subscription.serialize, response_deserializer=pubsub.Subscription.deserialize, @@ -308,7 +393,7 @@ def get_subscription( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "get_subscription" not in self._stubs: - self._stubs["get_subscription"] = self.grpc_channel.unary_unary( + self._stubs["get_subscription"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/GetSubscription", request_serializer=pubsub.GetSubscriptionRequest.serialize, response_deserializer=pubsub.Subscription.deserialize, @@ -337,7 +422,7 @@ def update_subscription( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "update_subscription" not in self._stubs: - self._stubs["update_subscription"] = self.grpc_channel.unary_unary( + self._stubs["update_subscription"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/UpdateSubscription", request_serializer=pubsub.UpdateSubscriptionRequest.serialize, response_deserializer=pubsub.Subscription.deserialize, @@ -365,7 +450,7 @@ def list_subscriptions( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "list_subscriptions" not in self._stubs: - self._stubs["list_subscriptions"] = self.grpc_channel.unary_unary( + self._stubs["list_subscriptions"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/ListSubscriptions", request_serializer=pubsub.ListSubscriptionsRequest.serialize, response_deserializer=pubsub.ListSubscriptionsResponse.deserialize, @@ -396,7 +481,7 @@ def delete_subscription( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "delete_subscription" not in self._stubs: - self._stubs["delete_subscription"] = self.grpc_channel.unary_unary( + self._stubs["delete_subscription"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/DeleteSubscription", request_serializer=pubsub.DeleteSubscriptionRequest.serialize, response_deserializer=empty_pb2.Empty.FromString, @@ -427,7 +512,7 @@ def modify_ack_deadline( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "modify_ack_deadline" not in self._stubs: - self._stubs["modify_ack_deadline"] = self.grpc_channel.unary_unary( + self._stubs["modify_ack_deadline"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/ModifyAckDeadline", request_serializer=pubsub.ModifyAckDeadlineRequest.serialize, response_deserializer=empty_pb2.Empty.FromString, @@ -460,7 +545,7 @@ def acknowledge( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "acknowledge" not in self._stubs: - self._stubs["acknowledge"] = self.grpc_channel.unary_unary( + self._stubs["acknowledge"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/Acknowledge", request_serializer=pubsub.AcknowledgeRequest.serialize, response_deserializer=empty_pb2.Empty.FromString, @@ -484,7 +569,7 @@ def pull(self) -> Callable[[pubsub.PullRequest], Awaitable[pubsub.PullResponse]] # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "pull" not in self._stubs: - self._stubs["pull"] = self.grpc_channel.unary_unary( + self._stubs["pull"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/Pull", request_serializer=pubsub.PullRequest.serialize, response_deserializer=pubsub.PullResponse.deserialize, @@ -519,7 +604,7 @@ def streaming_pull( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "streaming_pull" not in self._stubs: - self._stubs["streaming_pull"] = self.grpc_channel.stream_stream( + self._stubs["streaming_pull"] = self._logged_channel.stream_stream( "/google.pubsub.v1.Subscriber/StreamingPull", request_serializer=pubsub.StreamingPullRequest.serialize, response_deserializer=pubsub.StreamingPullResponse.deserialize, @@ -551,7 +636,7 @@ def modify_push_config( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "modify_push_config" not in self._stubs: - self._stubs["modify_push_config"] = self.grpc_channel.unary_unary( + self._stubs["modify_push_config"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/ModifyPushConfig", request_serializer=pubsub.ModifyPushConfigRequest.serialize, response_deserializer=empty_pb2.Empty.FromString, @@ -582,7 +667,7 @@ def get_snapshot( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "get_snapshot" not in self._stubs: - self._stubs["get_snapshot"] = self.grpc_channel.unary_unary( + self._stubs["get_snapshot"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/GetSnapshot", request_serializer=pubsub.GetSnapshotRequest.serialize, response_deserializer=pubsub.Snapshot.deserialize, @@ -614,7 +699,7 @@ def list_snapshots( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "list_snapshots" not in self._stubs: - self._stubs["list_snapshots"] = self.grpc_channel.unary_unary( + self._stubs["list_snapshots"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/ListSnapshots", request_serializer=pubsub.ListSnapshotsRequest.serialize, response_deserializer=pubsub.ListSnapshotsResponse.deserialize, @@ -658,7 +743,7 @@ def create_snapshot( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "create_snapshot" not in self._stubs: - self._stubs["create_snapshot"] = self.grpc_channel.unary_unary( + self._stubs["create_snapshot"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/CreateSnapshot", request_serializer=pubsub.CreateSnapshotRequest.serialize, response_deserializer=pubsub.Snapshot.deserialize, @@ -689,7 +774,7 @@ def update_snapshot( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "update_snapshot" not in self._stubs: - self._stubs["update_snapshot"] = self.grpc_channel.unary_unary( + self._stubs["update_snapshot"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/UpdateSnapshot", request_serializer=pubsub.UpdateSnapshotRequest.serialize, response_deserializer=pubsub.Snapshot.deserialize, @@ -724,7 +809,7 @@ def delete_snapshot( # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "delete_snapshot" not in self._stubs: - self._stubs["delete_snapshot"] = self.grpc_channel.unary_unary( + self._stubs["delete_snapshot"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/DeleteSnapshot", request_serializer=pubsub.DeleteSnapshotRequest.serialize, response_deserializer=empty_pb2.Empty.FromString, @@ -756,93 +841,13 @@ def seek(self) -> Callable[[pubsub.SeekRequest], Awaitable[pubsub.SeekResponse]] # gRPC handles serialization and deserialization, so we just need # to pass in the functions for each. if "seek" not in self._stubs: - self._stubs["seek"] = self.grpc_channel.unary_unary( + self._stubs["seek"] = self._logged_channel.unary_unary( "/google.pubsub.v1.Subscriber/Seek", request_serializer=pubsub.SeekRequest.serialize, response_deserializer=pubsub.SeekResponse.deserialize, ) return self._stubs["seek"] - @property - def set_iam_policy( - self, - ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: - r"""Return a callable for the set iam policy method over gRPC. - Sets the IAM access control policy on the specified - function. Replaces any existing policy. - Returns: - Callable[[~.SetIamPolicyRequest], - Awaitable[~.Policy]]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "set_iam_policy" not in self._stubs: - self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary( - "/google.iam.v1.IAMPolicy/SetIamPolicy", - request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, - response_deserializer=policy_pb2.Policy.FromString, - ) - return self._stubs["set_iam_policy"] - - @property - def get_iam_policy( - self, - ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], Awaitable[policy_pb2.Policy]]: - r"""Return a callable for the get iam policy method over gRPC. - Gets the IAM access control policy for a function. - Returns an empty policy if the function exists and does - not have a policy set. - Returns: - Callable[[~.GetIamPolicyRequest], - Awaitable[~.Policy]]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "get_iam_policy" not in self._stubs: - self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary( - "/google.iam.v1.IAMPolicy/GetIamPolicy", - request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, - response_deserializer=policy_pb2.Policy.FromString, - ) - return self._stubs["get_iam_policy"] - - @property - def test_iam_permissions( - self, - ) -> Callable[ - [iam_policy_pb2.TestIamPermissionsRequest], - Awaitable[iam_policy_pb2.TestIamPermissionsResponse], - ]: - r"""Return a callable for the test iam permissions method over gRPC. - Tests the specified permissions against the IAM access control - policy for a function. If the function does not exist, this will - return an empty set of permissions, not a NOT_FOUND error. - Returns: - Callable[[~.TestIamPermissionsRequest], - Awaitable[~.TestIamPermissionsResponse]]: - A function that, when called, will call the underlying RPC - on the server. - """ - # Generate a "stub function" on-the-fly which will actually make - # the request. - # gRPC handles serialization and deserialization, so we just need - # to pass in the functions for each. - if "test_iam_permissions" not in self._stubs: - self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary( - "/google.iam.v1.IAMPolicy/TestIamPermissions", - request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, - response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, - ) - return self._stubs["test_iam_permissions"] - def _prep_wrapped_messages(self, client_info): """Precompute the wrapped methods, overriding the base class method to use async wrappers.""" self._wrapped_methods = { @@ -1112,11 +1117,91 @@ def _wrap_method(self, func, *args, **kwargs): return gapic_v1.method_async.wrap_method(func, *args, **kwargs) def close(self): - return self.grpc_channel.close() + return self._logged_channel.close() @property def kind(self) -> str: return "grpc_asyncio" + @property + def set_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.SetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the set iam policy method over gRPC. + Sets the IAM access control policy on the specified + function. Replaces any existing policy. + Returns: + Callable[[~.SetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "set_iam_policy" not in self._stubs: + self._stubs["set_iam_policy"] = self._logged_channel.unary_unary( + "/google.iam.v1.IAMPolicy/SetIamPolicy", + request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["set_iam_policy"] + + @property + def get_iam_policy( + self, + ) -> Callable[[iam_policy_pb2.GetIamPolicyRequest], policy_pb2.Policy]: + r"""Return a callable for the get iam policy method over gRPC. + Gets the IAM access control policy for a function. + Returns an empty policy if the function exists and does + not have a policy set. + Returns: + Callable[[~.GetIamPolicyRequest], + ~.Policy]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_iam_policy" not in self._stubs: + self._stubs["get_iam_policy"] = self._logged_channel.unary_unary( + "/google.iam.v1.IAMPolicy/GetIamPolicy", + request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString, + response_deserializer=policy_pb2.Policy.FromString, + ) + return self._stubs["get_iam_policy"] + + @property + def test_iam_permissions( + self, + ) -> Callable[ + [iam_policy_pb2.TestIamPermissionsRequest], + iam_policy_pb2.TestIamPermissionsResponse, + ]: + r"""Return a callable for the test iam permissions method over gRPC. + Tests the specified permissions against the IAM access control + policy for a function. If the function does not exist, this will + return an empty set of permissions, not a NOT_FOUND error. + Returns: + Callable[[~.TestIamPermissionsRequest], + ~.TestIamPermissionsResponse]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "test_iam_permissions" not in self._stubs: + self._stubs["test_iam_permissions"] = self._logged_channel.unary_unary( + "/google.iam.v1.IAMPolicy/TestIamPermissions", + request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString, + response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString, + ) + return self._stubs["test_iam_permissions"] + __all__ = ("SubscriberGrpcAsyncIOTransport",) diff --git a/google/pubsub_v1/services/subscriber/transports/rest.py b/google/pubsub_v1/services/subscriber/transports/rest.py index 376fd4ab3..a44591cf9 100644 --- a/google/pubsub_v1/services/subscriber/transports/rest.py +++ b/google/pubsub_v1/services/subscriber/transports/rest.py @@ -13,9 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import logging +import json # type: ignore from google.auth.transport.requests import AuthorizedSession # type: ignore -import json # type: ignore from google.auth import credentials as ga_credentials # type: ignore from google.api_core import exceptions as core_exceptions from google.api_core import retry as retries @@ -33,8 +34,6 @@ import warnings -from google.iam.v1 import iam_policy_pb2 # type: ignore -from google.iam.v1 import policy_pb2 # type: ignore from google.protobuf import empty_pb2 # type: ignore from google.pubsub_v1.types import pubsub @@ -47,6 +46,14 @@ except AttributeError: # pragma: NO COVER OptionalRetry = Union[retries.Retry, object, None] # type: ignore +try: + from google.api_core import client_logging # type: ignore + + CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER +except ImportError: # pragma: NO COVER + CLIENT_LOGGING_SUPPORTED = False + +_LOGGER = logging.getLogger(__name__) DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( gapic_version=BASE_DEFAULT_CLIENT_INFO.gapic_version, @@ -177,8 +184,10 @@ def post_update_subscription(self, response): """ def pre_acknowledge( - self, request: pubsub.AcknowledgeRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[pubsub.AcknowledgeRequest, Sequence[Tuple[str, str]]]: + self, + request: pubsub.AcknowledgeRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.AcknowledgeRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for acknowledge Override in a subclass to manipulate the request or metadata @@ -187,8 +196,10 @@ def pre_acknowledge( return request, metadata def pre_create_snapshot( - self, request: pubsub.CreateSnapshotRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[pubsub.CreateSnapshotRequest, Sequence[Tuple[str, str]]]: + self, + request: pubsub.CreateSnapshotRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.CreateSnapshotRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for create_snapshot Override in a subclass to manipulate the request or metadata @@ -199,15 +210,40 @@ def pre_create_snapshot( def post_create_snapshot(self, response: pubsub.Snapshot) -> pubsub.Snapshot: """Post-rpc interceptor for create_snapshot - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_create_snapshot_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Subscriber server but before - it is returned to user code. + it is returned to user code. This `post_create_snapshot` interceptor runs + before the `post_create_snapshot_with_metadata` interceptor. """ return response + def post_create_snapshot_with_metadata( + self, + response: pubsub.Snapshot, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.Snapshot, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for create_snapshot + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Subscriber server but before it is returned to user code. + + We recommend only using this `post_create_snapshot_with_metadata` + interceptor in new development instead of the `post_create_snapshot` interceptor. + When both interceptors are used, this `post_create_snapshot_with_metadata` interceptor runs after the + `post_create_snapshot` interceptor. The (possibly modified) response returned by + `post_create_snapshot` will be passed to + `post_create_snapshot_with_metadata`. + """ + return response, metadata + def pre_create_subscription( - self, request: pubsub.Subscription, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[pubsub.Subscription, Sequence[Tuple[str, str]]]: + self, + request: pubsub.Subscription, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.Subscription, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for create_subscription Override in a subclass to manipulate the request or metadata @@ -220,15 +256,40 @@ def post_create_subscription( ) -> pubsub.Subscription: """Post-rpc interceptor for create_subscription - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_create_subscription_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Subscriber server but before - it is returned to user code. + it is returned to user code. This `post_create_subscription` interceptor runs + before the `post_create_subscription_with_metadata` interceptor. """ return response + def post_create_subscription_with_metadata( + self, + response: pubsub.Subscription, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.Subscription, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for create_subscription + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Subscriber server but before it is returned to user code. + + We recommend only using this `post_create_subscription_with_metadata` + interceptor in new development instead of the `post_create_subscription` interceptor. + When both interceptors are used, this `post_create_subscription_with_metadata` interceptor runs after the + `post_create_subscription` interceptor. The (possibly modified) response returned by + `post_create_subscription` will be passed to + `post_create_subscription_with_metadata`. + """ + return response, metadata + def pre_delete_snapshot( - self, request: pubsub.DeleteSnapshotRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[pubsub.DeleteSnapshotRequest, Sequence[Tuple[str, str]]]: + self, + request: pubsub.DeleteSnapshotRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.DeleteSnapshotRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for delete_snapshot Override in a subclass to manipulate the request or metadata @@ -239,8 +300,10 @@ def pre_delete_snapshot( def pre_delete_subscription( self, request: pubsub.DeleteSubscriptionRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[pubsub.DeleteSubscriptionRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + pubsub.DeleteSubscriptionRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for delete_subscription Override in a subclass to manipulate the request or metadata @@ -249,8 +312,10 @@ def pre_delete_subscription( return request, metadata def pre_get_snapshot( - self, request: pubsub.GetSnapshotRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[pubsub.GetSnapshotRequest, Sequence[Tuple[str, str]]]: + self, + request: pubsub.GetSnapshotRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.GetSnapshotRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for get_snapshot Override in a subclass to manipulate the request or metadata @@ -261,17 +326,40 @@ def pre_get_snapshot( def post_get_snapshot(self, response: pubsub.Snapshot) -> pubsub.Snapshot: """Post-rpc interceptor for get_snapshot - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_get_snapshot_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Subscriber server but before - it is returned to user code. + it is returned to user code. This `post_get_snapshot` interceptor runs + before the `post_get_snapshot_with_metadata` interceptor. """ return response + def post_get_snapshot_with_metadata( + self, + response: pubsub.Snapshot, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.Snapshot, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for get_snapshot + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Subscriber server but before it is returned to user code. + + We recommend only using this `post_get_snapshot_with_metadata` + interceptor in new development instead of the `post_get_snapshot` interceptor. + When both interceptors are used, this `post_get_snapshot_with_metadata` interceptor runs after the + `post_get_snapshot` interceptor. The (possibly modified) response returned by + `post_get_snapshot` will be passed to + `post_get_snapshot_with_metadata`. + """ + return response, metadata + def pre_get_subscription( self, request: pubsub.GetSubscriptionRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[pubsub.GetSubscriptionRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.GetSubscriptionRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for get_subscription Override in a subclass to manipulate the request or metadata @@ -284,15 +372,40 @@ def post_get_subscription( ) -> pubsub.Subscription: """Post-rpc interceptor for get_subscription - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_get_subscription_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Subscriber server but before - it is returned to user code. + it is returned to user code. This `post_get_subscription` interceptor runs + before the `post_get_subscription_with_metadata` interceptor. """ return response + def post_get_subscription_with_metadata( + self, + response: pubsub.Subscription, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.Subscription, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for get_subscription + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Subscriber server but before it is returned to user code. + + We recommend only using this `post_get_subscription_with_metadata` + interceptor in new development instead of the `post_get_subscription` interceptor. + When both interceptors are used, this `post_get_subscription_with_metadata` interceptor runs after the + `post_get_subscription` interceptor. The (possibly modified) response returned by + `post_get_subscription` will be passed to + `post_get_subscription_with_metadata`. + """ + return response, metadata + def pre_list_snapshots( - self, request: pubsub.ListSnapshotsRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[pubsub.ListSnapshotsRequest, Sequence[Tuple[str, str]]]: + self, + request: pubsub.ListSnapshotsRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.ListSnapshotsRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for list_snapshots Override in a subclass to manipulate the request or metadata @@ -305,17 +418,42 @@ def post_list_snapshots( ) -> pubsub.ListSnapshotsResponse: """Post-rpc interceptor for list_snapshots - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_list_snapshots_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Subscriber server but before - it is returned to user code. + it is returned to user code. This `post_list_snapshots` interceptor runs + before the `post_list_snapshots_with_metadata` interceptor. """ return response + def post_list_snapshots_with_metadata( + self, + response: pubsub.ListSnapshotsResponse, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.ListSnapshotsResponse, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for list_snapshots + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Subscriber server but before it is returned to user code. + + We recommend only using this `post_list_snapshots_with_metadata` + interceptor in new development instead of the `post_list_snapshots` interceptor. + When both interceptors are used, this `post_list_snapshots_with_metadata` interceptor runs after the + `post_list_snapshots` interceptor. The (possibly modified) response returned by + `post_list_snapshots` will be passed to + `post_list_snapshots_with_metadata`. + """ + return response, metadata + def pre_list_subscriptions( self, request: pubsub.ListSubscriptionsRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[pubsub.ListSubscriptionsRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + pubsub.ListSubscriptionsRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for list_subscriptions Override in a subclass to manipulate the request or metadata @@ -328,17 +466,44 @@ def post_list_subscriptions( ) -> pubsub.ListSubscriptionsResponse: """Post-rpc interceptor for list_subscriptions - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_list_subscriptions_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Subscriber server but before - it is returned to user code. + it is returned to user code. This `post_list_subscriptions` interceptor runs + before the `post_list_subscriptions_with_metadata` interceptor. """ return response + def post_list_subscriptions_with_metadata( + self, + response: pubsub.ListSubscriptionsResponse, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + pubsub.ListSubscriptionsResponse, Sequence[Tuple[str, Union[str, bytes]]] + ]: + """Post-rpc interceptor for list_subscriptions + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Subscriber server but before it is returned to user code. + + We recommend only using this `post_list_subscriptions_with_metadata` + interceptor in new development instead of the `post_list_subscriptions` interceptor. + When both interceptors are used, this `post_list_subscriptions_with_metadata` interceptor runs after the + `post_list_subscriptions` interceptor. The (possibly modified) response returned by + `post_list_subscriptions` will be passed to + `post_list_subscriptions_with_metadata`. + """ + return response, metadata + def pre_modify_ack_deadline( self, request: pubsub.ModifyAckDeadlineRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[pubsub.ModifyAckDeadlineRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + pubsub.ModifyAckDeadlineRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for modify_ack_deadline Override in a subclass to manipulate the request or metadata @@ -349,8 +514,8 @@ def pre_modify_ack_deadline( def pre_modify_push_config( self, request: pubsub.ModifyPushConfigRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[pubsub.ModifyPushConfigRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.ModifyPushConfigRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for modify_push_config Override in a subclass to manipulate the request or metadata @@ -359,8 +524,10 @@ def pre_modify_push_config( return request, metadata def pre_pull( - self, request: pubsub.PullRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[pubsub.PullRequest, Sequence[Tuple[str, str]]]: + self, + request: pubsub.PullRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.PullRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for pull Override in a subclass to manipulate the request or metadata @@ -371,15 +538,40 @@ def pre_pull( def post_pull(self, response: pubsub.PullResponse) -> pubsub.PullResponse: """Post-rpc interceptor for pull - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_pull_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Subscriber server but before - it is returned to user code. + it is returned to user code. This `post_pull` interceptor runs + before the `post_pull_with_metadata` interceptor. """ return response + def post_pull_with_metadata( + self, + response: pubsub.PullResponse, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.PullResponse, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for pull + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Subscriber server but before it is returned to user code. + + We recommend only using this `post_pull_with_metadata` + interceptor in new development instead of the `post_pull` interceptor. + When both interceptors are used, this `post_pull_with_metadata` interceptor runs after the + `post_pull` interceptor. The (possibly modified) response returned by + `post_pull` will be passed to + `post_pull_with_metadata`. + """ + return response, metadata + def pre_seek( - self, request: pubsub.SeekRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[pubsub.SeekRequest, Sequence[Tuple[str, str]]]: + self, + request: pubsub.SeekRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.SeekRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for seek Override in a subclass to manipulate the request or metadata @@ -390,15 +582,40 @@ def pre_seek( def post_seek(self, response: pubsub.SeekResponse) -> pubsub.SeekResponse: """Post-rpc interceptor for seek - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_seek_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Subscriber server but before - it is returned to user code. + it is returned to user code. This `post_seek` interceptor runs + before the `post_seek_with_metadata` interceptor. """ return response + def post_seek_with_metadata( + self, + response: pubsub.SeekResponse, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.SeekResponse, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for seek + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Subscriber server but before it is returned to user code. + + We recommend only using this `post_seek_with_metadata` + interceptor in new development instead of the `post_seek` interceptor. + When both interceptors are used, this `post_seek_with_metadata` interceptor runs after the + `post_seek` interceptor. The (possibly modified) response returned by + `post_seek` will be passed to + `post_seek_with_metadata`. + """ + return response, metadata + def pre_update_snapshot( - self, request: pubsub.UpdateSnapshotRequest, metadata: Sequence[Tuple[str, str]] - ) -> Tuple[pubsub.UpdateSnapshotRequest, Sequence[Tuple[str, str]]]: + self, + request: pubsub.UpdateSnapshotRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.UpdateSnapshotRequest, Sequence[Tuple[str, Union[str, bytes]]]]: """Pre-rpc interceptor for update_snapshot Override in a subclass to manipulate the request or metadata @@ -409,17 +626,42 @@ def pre_update_snapshot( def post_update_snapshot(self, response: pubsub.Snapshot) -> pubsub.Snapshot: """Post-rpc interceptor for update_snapshot - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_update_snapshot_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Subscriber server but before - it is returned to user code. + it is returned to user code. This `post_update_snapshot` interceptor runs + before the `post_update_snapshot_with_metadata` interceptor. """ return response + def post_update_snapshot_with_metadata( + self, + response: pubsub.Snapshot, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.Snapshot, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for update_snapshot + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Subscriber server but before it is returned to user code. + + We recommend only using this `post_update_snapshot_with_metadata` + interceptor in new development instead of the `post_update_snapshot` interceptor. + When both interceptors are used, this `post_update_snapshot_with_metadata` interceptor runs after the + `post_update_snapshot` interceptor. The (possibly modified) response returned by + `post_update_snapshot` will be passed to + `post_update_snapshot_with_metadata`. + """ + return response, metadata + def pre_update_subscription( self, request: pubsub.UpdateSubscriptionRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[pubsub.UpdateSubscriptionRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + pubsub.UpdateSubscriptionRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for update_subscription Override in a subclass to manipulate the request or metadata @@ -432,17 +674,42 @@ def post_update_subscription( ) -> pubsub.Subscription: """Post-rpc interceptor for update_subscription - Override in a subclass to manipulate the response + DEPRECATED. Please use the `post_update_subscription_with_metadata` + interceptor instead. + + Override in a subclass to read or manipulate the response after it is returned by the Subscriber server but before - it is returned to user code. + it is returned to user code. This `post_update_subscription` interceptor runs + before the `post_update_subscription_with_metadata` interceptor. """ return response + def post_update_subscription_with_metadata( + self, + response: pubsub.Subscription, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[pubsub.Subscription, Sequence[Tuple[str, Union[str, bytes]]]]: + """Post-rpc interceptor for update_subscription + + Override in a subclass to read or manipulate the response or metadata after it + is returned by the Subscriber server but before it is returned to user code. + + We recommend only using this `post_update_subscription_with_metadata` + interceptor in new development instead of the `post_update_subscription` interceptor. + When both interceptors are used, this `post_update_subscription_with_metadata` interceptor runs after the + `post_update_subscription` interceptor. The (possibly modified) response returned by + `post_update_subscription` will be passed to + `post_update_subscription_with_metadata`. + """ + return response, metadata + def pre_get_iam_policy( self, request: iam_policy_pb2.GetIamPolicyRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[iam_policy_pb2.GetIamPolicyRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + iam_policy_pb2.GetIamPolicyRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for get_iam_policy Override in a subclass to manipulate the request or metadata @@ -462,8 +729,10 @@ def post_get_iam_policy(self, response: policy_pb2.Policy) -> policy_pb2.Policy: def pre_set_iam_policy( self, request: iam_policy_pb2.SetIamPolicyRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[iam_policy_pb2.SetIamPolicyRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + iam_policy_pb2.SetIamPolicyRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: """Pre-rpc interceptor for set_iam_policy Override in a subclass to manipulate the request or metadata @@ -483,8 +752,11 @@ def post_set_iam_policy(self, response: policy_pb2.Policy) -> policy_pb2.Policy: def pre_test_iam_permissions( self, request: iam_policy_pb2.TestIamPermissionsRequest, - metadata: Sequence[Tuple[str, str]], - ) -> Tuple[iam_policy_pb2.TestIamPermissionsRequest, Sequence[Tuple[str, str]]]: + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + iam_policy_pb2.TestIamPermissionsRequest, + Sequence[Tuple[str, Union[str, bytes]]], + ]: """Pre-rpc interceptor for test_iam_permissions Override in a subclass to manipulate the request or metadata @@ -628,7 +900,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ): r"""Call the acknowledge method over HTTP. @@ -638,13 +910,16 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ http_options = ( _BaseSubscriberRestTransport._BaseAcknowledge._get_http_options() ) + request, metadata = self._interceptor.pre_acknowledge(request, metadata) transcoded_request = ( _BaseSubscriberRestTransport._BaseAcknowledge._get_transcoded_request( @@ -663,6 +938,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.Acknowledge", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "Acknowledge", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._Acknowledge._get_response( self._host, @@ -714,7 +1016,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Snapshot: r"""Call the create snapshot method over HTTP. @@ -724,8 +1026,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.Snapshot: @@ -741,6 +1045,7 @@ def __call__( http_options = ( _BaseSubscriberRestTransport._BaseCreateSnapshot._get_http_options() ) + request, metadata = self._interceptor.pre_create_snapshot(request, metadata) transcoded_request = _BaseSubscriberRestTransport._BaseCreateSnapshot._get_transcoded_request( http_options, request @@ -759,6 +1064,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.CreateSnapshot", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "CreateSnapshot", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._CreateSnapshot._get_response( self._host, @@ -780,7 +1112,33 @@ def __call__( pb_resp = pubsub.Snapshot.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_create_snapshot(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_create_snapshot_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.Snapshot.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SubscriberClient.create_snapshot", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "CreateSnapshot", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _CreateSubscription( @@ -818,7 +1176,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Subscription: r"""Call the create subscription method over HTTP. @@ -831,8 +1189,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.Subscription: @@ -846,6 +1206,7 @@ def __call__( http_options = ( _BaseSubscriberRestTransport._BaseCreateSubscription._get_http_options() ) + request, metadata = self._interceptor.pre_create_subscription( request, metadata ) @@ -862,6 +1223,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.CreateSubscription", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "CreateSubscription", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._CreateSubscription._get_response( self._host, @@ -883,7 +1271,33 @@ def __call__( pb_resp = pubsub.Subscription.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_create_subscription(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_create_subscription_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.Subscription.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SubscriberClient.create_subscription", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "CreateSubscription", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _DeleteSnapshot( @@ -920,7 +1334,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ): r"""Call the delete snapshot method over HTTP. @@ -930,13 +1344,16 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ http_options = ( _BaseSubscriberRestTransport._BaseDeleteSnapshot._get_http_options() ) + request, metadata = self._interceptor.pre_delete_snapshot(request, metadata) transcoded_request = _BaseSubscriberRestTransport._BaseDeleteSnapshot._get_transcoded_request( http_options, request @@ -949,6 +1366,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.DeleteSnapshot", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "DeleteSnapshot", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._DeleteSnapshot._get_response( self._host, @@ -998,7 +1442,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ): r"""Call the delete subscription method over HTTP. @@ -1009,13 +1453,16 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ http_options = ( _BaseSubscriberRestTransport._BaseDeleteSubscription._get_http_options() ) + request, metadata = self._interceptor.pre_delete_subscription( request, metadata ) @@ -1028,6 +1475,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.DeleteSubscription", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "DeleteSubscription", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._DeleteSubscription._get_response( self._host, @@ -1077,7 +1551,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Snapshot: r"""Call the get snapshot method over HTTP. @@ -1087,8 +1561,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.Snapshot: @@ -1104,6 +1580,7 @@ def __call__( http_options = ( _BaseSubscriberRestTransport._BaseGetSnapshot._get_http_options() ) + request, metadata = self._interceptor.pre_get_snapshot(request, metadata) transcoded_request = ( _BaseSubscriberRestTransport._BaseGetSnapshot._get_transcoded_request( @@ -1118,6 +1595,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.GetSnapshot", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "GetSnapshot", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._GetSnapshot._get_response( self._host, @@ -1138,7 +1642,33 @@ def __call__( pb_resp = pubsub.Snapshot.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_get_snapshot(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_get_snapshot_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.Snapshot.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SubscriberClient.get_snapshot", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "GetSnapshot", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _GetSubscription( @@ -1175,7 +1705,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Subscription: r"""Call the get subscription method over HTTP. @@ -1186,8 +1716,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.Subscription: @@ -1201,6 +1733,7 @@ def __call__( http_options = ( _BaseSubscriberRestTransport._BaseGetSubscription._get_http_options() ) + request, metadata = self._interceptor.pre_get_subscription( request, metadata ) @@ -1213,6 +1746,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.GetSubscription", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "GetSubscription", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._GetSubscription._get_response( self._host, @@ -1233,7 +1793,33 @@ def __call__( pb_resp = pubsub.Subscription.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_get_subscription(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_get_subscription_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.Subscription.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SubscriberClient.get_subscription", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "GetSubscription", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _ListSnapshots( @@ -1270,7 +1856,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.ListSnapshotsResponse: r"""Call the list snapshots method over HTTP. @@ -1280,8 +1866,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.ListSnapshotsResponse: @@ -1291,6 +1879,7 @@ def __call__( http_options = ( _BaseSubscriberRestTransport._BaseListSnapshots._get_http_options() ) + request, metadata = self._interceptor.pre_list_snapshots(request, metadata) transcoded_request = ( _BaseSubscriberRestTransport._BaseListSnapshots._get_transcoded_request( @@ -1305,6 +1894,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.ListSnapshots", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "ListSnapshots", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._ListSnapshots._get_response( self._host, @@ -1325,7 +1941,33 @@ def __call__( pb_resp = pubsub.ListSnapshotsResponse.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_list_snapshots(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_list_snapshots_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.ListSnapshotsResponse.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SubscriberClient.list_snapshots", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "ListSnapshots", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _ListSubscriptions( @@ -1362,7 +2004,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.ListSubscriptionsResponse: r"""Call the list subscriptions method over HTTP. @@ -1372,8 +2014,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.ListSubscriptionsResponse: @@ -1383,6 +2027,7 @@ def __call__( http_options = ( _BaseSubscriberRestTransport._BaseListSubscriptions._get_http_options() ) + request, metadata = self._interceptor.pre_list_subscriptions( request, metadata ) @@ -1395,6 +2040,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.ListSubscriptions", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "ListSubscriptions", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._ListSubscriptions._get_response( self._host, @@ -1415,7 +2087,35 @@ def __call__( pb_resp = pubsub.ListSubscriptionsResponse.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_list_subscriptions(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_list_subscriptions_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.ListSubscriptionsResponse.to_json( + response + ) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SubscriberClient.list_subscriptions", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "ListSubscriptions", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _ModifyAckDeadline( @@ -1453,7 +2153,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ): r"""Call the modify ack deadline method over HTTP. @@ -1464,13 +2164,16 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ http_options = ( _BaseSubscriberRestTransport._BaseModifyAckDeadline._get_http_options() ) + request, metadata = self._interceptor.pre_modify_ack_deadline( request, metadata ) @@ -1487,6 +2190,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.ModifyAckDeadline", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "ModifyAckDeadline", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._ModifyAckDeadline._get_response( self._host, @@ -1538,7 +2268,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ): r"""Call the modify push config method over HTTP. @@ -1549,13 +2279,16 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. """ http_options = ( _BaseSubscriberRestTransport._BaseModifyPushConfig._get_http_options() ) + request, metadata = self._interceptor.pre_modify_push_config( request, metadata ) @@ -1572,6 +2305,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.ModifyPushConfig", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "ModifyPushConfig", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._ModifyPushConfig._get_response( self._host, @@ -1621,7 +2381,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.PullResponse: r"""Call the pull method over HTTP. @@ -1631,8 +2391,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.PullResponse: @@ -1640,6 +2402,7 @@ def __call__( """ http_options = _BaseSubscriberRestTransport._BasePull._get_http_options() + request, metadata = self._interceptor.pre_pull(request, metadata) transcoded_request = ( _BaseSubscriberRestTransport._BasePull._get_transcoded_request( @@ -1658,6 +2421,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.Pull", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "Pull", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._Pull._get_response( self._host, @@ -1679,7 +2469,31 @@ def __call__( pb_resp = pubsub.PullResponse.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_pull(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_pull_with_metadata(resp, response_metadata) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.PullResponse.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SubscriberClient.pull", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "Pull", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _Seek(_BaseSubscriberRestTransport._BaseSeek, SubscriberRestStub): @@ -1715,7 +2529,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.SeekResponse: r"""Call the seek method over HTTP. @@ -1725,8 +2539,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.SeekResponse: @@ -1736,6 +2552,7 @@ def __call__( """ http_options = _BaseSubscriberRestTransport._BaseSeek._get_http_options() + request, metadata = self._interceptor.pre_seek(request, metadata) transcoded_request = ( _BaseSubscriberRestTransport._BaseSeek._get_transcoded_request( @@ -1754,6 +2571,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.Seek", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "Seek", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._Seek._get_response( self._host, @@ -1775,7 +2619,31 @@ def __call__( pb_resp = pubsub.SeekResponse.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_seek(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_seek_with_metadata(resp, response_metadata) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.SeekResponse.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SubscriberClient.seek", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "Seek", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _StreamingPull( @@ -1790,7 +2658,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> rest_streaming.ResponseIterator: raise NotImplementedError( "Method StreamingPull is not available over REST transport" @@ -1831,7 +2699,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Snapshot: r"""Call the update snapshot method over HTTP. @@ -1842,8 +2710,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.Snapshot: @@ -1859,6 +2729,7 @@ def __call__( http_options = ( _BaseSubscriberRestTransport._BaseUpdateSnapshot._get_http_options() ) + request, metadata = self._interceptor.pre_update_snapshot(request, metadata) transcoded_request = _BaseSubscriberRestTransport._BaseUpdateSnapshot._get_transcoded_request( http_options, request @@ -1877,6 +2748,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.UpdateSnapshot", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "UpdateSnapshot", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._UpdateSnapshot._get_response( self._host, @@ -1898,7 +2796,33 @@ def __call__( pb_resp = pubsub.Snapshot.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_update_snapshot(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_update_snapshot_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.Snapshot.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SubscriberClient.update_snapshot", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "UpdateSnapshot", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp class _UpdateSubscription( @@ -1936,7 +2860,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> pubsub.Subscription: r"""Call the update subscription method over HTTP. @@ -1947,8 +2871,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: ~.pubsub.Subscription: @@ -1962,6 +2888,7 @@ def __call__( http_options = ( _BaseSubscriberRestTransport._BaseUpdateSubscription._get_http_options() ) + request, metadata = self._interceptor.pre_update_subscription( request, metadata ) @@ -1978,6 +2905,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = type(request).to_json(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.UpdateSubscription", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "UpdateSubscription", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._UpdateSubscription._get_response( self._host, @@ -1999,7 +2953,33 @@ def __call__( pb_resp = pubsub.Subscription.pb(resp) json_format.Parse(response.content, pb_resp, ignore_unknown_fields=True) + resp = self._interceptor.post_update_subscription(resp) + response_metadata = [(k, str(v)) for k, v in response.headers.items()] + resp, _ = self._interceptor.post_update_subscription_with_metadata( + resp, response_metadata + ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = pubsub.Subscription.to_json(response) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SubscriberClient.update_subscription", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "UpdateSubscription", + "metadata": http_response["headers"], + "httpResponse": http_response, + }, + ) return resp @property @@ -2160,7 +3140,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Call the get iam policy method over HTTP. @@ -2170,8 +3150,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: policy_pb2.Policy: Response from GetIamPolicy method. @@ -2180,6 +3162,7 @@ def __call__( http_options = ( _BaseSubscriberRestTransport._BaseGetIamPolicy._get_http_options() ) + request, metadata = self._interceptor.pre_get_iam_policy(request, metadata) transcoded_request = ( _BaseSubscriberRestTransport._BaseGetIamPolicy._get_transcoded_request( @@ -2194,6 +3177,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.GetIamPolicy", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "GetIamPolicy", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._GetIamPolicy._get_response( self._host, @@ -2213,6 +3223,27 @@ def __call__( resp = policy_pb2.Policy() resp = json_format.Parse(content, resp) resp = self._interceptor.post_get_iam_policy(resp) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = json_format.MessageToJson(resp) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SubscriberAsyncClient.GetIamPolicy", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "GetIamPolicy", + "httpResponse": http_response, + "metadata": http_response["headers"], + }, + ) return resp @property @@ -2254,7 +3285,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> policy_pb2.Policy: r"""Call the set iam policy method over HTTP. @@ -2264,8 +3295,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: policy_pb2.Policy: Response from SetIamPolicy method. @@ -2274,6 +3307,7 @@ def __call__( http_options = ( _BaseSubscriberRestTransport._BaseSetIamPolicy._get_http_options() ) + request, metadata = self._interceptor.pre_set_iam_policy(request, metadata) transcoded_request = ( _BaseSubscriberRestTransport._BaseSetIamPolicy._get_transcoded_request( @@ -2294,6 +3328,33 @@ def __call__( ) ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.SetIamPolicy", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "SetIamPolicy", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._SetIamPolicy._get_response( self._host, @@ -2314,6 +3375,27 @@ def __call__( resp = policy_pb2.Policy() resp = json_format.Parse(content, resp) resp = self._interceptor.post_set_iam_policy(resp) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = json_format.MessageToJson(resp) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SubscriberAsyncClient.SetIamPolicy", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "SetIamPolicy", + "httpResponse": http_response, + "metadata": http_response["headers"], + }, + ) return resp @property @@ -2355,7 +3437,7 @@ def __call__( *, retry: OptionalRetry = gapic_v1.method.DEFAULT, timeout: Optional[float] = None, - metadata: Sequence[Tuple[str, str]] = (), + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> iam_policy_pb2.TestIamPermissionsResponse: r"""Call the test iam permissions method over HTTP. @@ -2365,8 +3447,10 @@ def __call__( retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. timeout (float): The timeout for this request. - metadata (Sequence[Tuple[str, str]]): Strings which should be - sent along with the request as metadata. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. Returns: iam_policy_pb2.TestIamPermissionsResponse: Response from TestIamPermissions method. @@ -2375,6 +3459,7 @@ def __call__( http_options = ( _BaseSubscriberRestTransport._BaseTestIamPermissions._get_http_options() ) + request, metadata = self._interceptor.pre_test_iam_permissions( request, metadata ) @@ -2391,6 +3476,33 @@ def __call__( transcoded_request ) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.pubsub_v1.SubscriberClient.TestIamPermissions", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "TestIamPermissions", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + # Send the request response = SubscriberRestTransport._TestIamPermissions._get_response( self._host, @@ -2411,6 +3523,27 @@ def __call__( resp = iam_policy_pb2.TestIamPermissionsResponse() resp = json_format.Parse(content, resp) resp = self._interceptor.post_test_iam_permissions(resp) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = json_format.MessageToJson(resp) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.pubsub_v1.SubscriberAsyncClient.TestIamPermissions", + extra={ + "serviceName": "google.pubsub.v1.Subscriber", + "rpcName": "TestIamPermissions", + "httpResponse": http_response, + "metadata": http_response["headers"], + }, + ) return resp @property diff --git a/google/pubsub_v1/services/subscriber/transports/rest_base.py b/google/pubsub_v1/services/subscriber/transports/rest_base.py index 6626a04c1..033af0b16 100644 --- a/google/pubsub_v1/services/subscriber/transports/rest_base.py +++ b/google/pubsub_v1/services/subscriber/transports/rest_base.py @@ -26,8 +26,6 @@ from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union -from google.iam.v1 import iam_policy_pb2 # type: ignore -from google.iam.v1 import policy_pb2 # type: ignore from google.protobuf import empty_pb2 # type: ignore from google.pubsub_v1.types import pubsub diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index 5f15e445b..8917db59f 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -1099,35 +1099,33 @@ class JavaScriptUDF(proto.Message): Required. JavaScript code that contains a function ``function_name`` with the below signature: - :: - // /** - // * Transforms a Pub/Sub message. - // - // * @return {(Object)>|null)} - To - // * filter a message, return `null`. To transform a message return a map - // * with the following keys: - // * - (required) 'data' : {string} - // * - (optional) 'attributes' : {Object} - // * Returning empty `attributes` will remove all attributes from the - // * message. - // * - // * @param {(Object)>} Pub/Sub - // * message. Keys: - // * - (required) 'data' : {string} - // * - (required) 'attributes' : {Object} - // * - // * @param {Object} metadata - Pub/Sub message metadata. - // * Keys: - // * - (required) 'message_id' : {string} - // * - (optional) 'publish_time': {string} YYYY-MM-DDTHH:MM:SSZ format - // * - (optional) 'ordering_key': {string} - // */ - // - // function (message, metadata) { - // } - + /** + * Transforms a Pub/Sub message. + + * @return {(Object)>|null)} - To + * filter a message, return `null`. To transform a message return a map + * with the following keys: + * - (required) 'data' : {string} + * - (optional) 'attributes' : {Object} + * Returning empty `attributes` will remove all attributes from the + * message. + * + * @param {(Object)>} Pub/Sub + * message. Keys: + * - (required) 'data' : {string} + * - (required) 'attributes' : {Object} + * + * @param {Object} metadata - Pub/Sub message metadata. + * Keys: + * - (required) 'message_id' : {string} + * - (optional) 'publish_time': {string} YYYY-MM-DDTHH:MM:SSZ format + * - (optional) 'ordering_key': {string} + */ + + function (message, metadata) { + } """ function_name: str = proto.Field( @@ -1153,9 +1151,11 @@ class MessageTransform(proto.Message): This field is a member of `oneof`_ ``transform``. enabled (bool): - Optional. If set to true, the transform is enabled. If - false, the transform is disabled and will not be applied to - messages. Defaults to ``true``. + Optional. This field is deprecated, use the ``disabled`` + field to disable transforms. + disabled (bool): + Optional. If true, the transform is disabled and will not be + applied to messages. Defaults to ``false``. """ javascript_udf: "JavaScriptUDF" = proto.Field( @@ -1168,6 +1168,10 @@ class MessageTransform(proto.Message): proto.BOOL, number=3, ) + disabled: bool = proto.Field( + proto.BOOL, + number=4, + ) class Topic(proto.Message): diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 039f0ca3d..dd0d6423a 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.28.0" + "version": "0.1.0" }, "snippets": [ { @@ -47,7 +47,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Topic", @@ -127,7 +127,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Topic", @@ -208,7 +208,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "shortName": "delete_topic" @@ -285,7 +285,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "shortName": "delete_topic" @@ -359,7 +359,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.DetachSubscriptionResponse", @@ -435,7 +435,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.DetachSubscriptionResponse", @@ -516,7 +516,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Topic", @@ -596,7 +596,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Topic", @@ -677,7 +677,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.services.publisher.pagers.ListTopicSnapshotsAsyncPager", @@ -757,7 +757,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.services.publisher.pagers.ListTopicSnapshotsPager", @@ -838,7 +838,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.services.publisher.pagers.ListTopicSubscriptionsAsyncPager", @@ -918,7 +918,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.services.publisher.pagers.ListTopicSubscriptionsPager", @@ -999,7 +999,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.services.publisher.pagers.ListTopicsAsyncPager", @@ -1079,7 +1079,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.services.publisher.pagers.ListTopicsPager", @@ -1164,7 +1164,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.PublishResponse", @@ -1248,7 +1248,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.PublishResponse", @@ -1333,7 +1333,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Topic", @@ -1417,7 +1417,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Topic", @@ -1502,7 +1502,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Schema", @@ -1586,7 +1586,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Schema", @@ -1675,7 +1675,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Schema", @@ -1763,7 +1763,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Schema", @@ -1848,7 +1848,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Schema", @@ -1932,7 +1932,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Schema", @@ -2013,7 +2013,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "shortName": "delete_schema" @@ -2090,7 +2090,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "shortName": "delete_schema" @@ -2168,7 +2168,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Schema", @@ -2248,7 +2248,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Schema", @@ -2329,7 +2329,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.services.schema_service.pagers.ListSchemaRevisionsAsyncPager", @@ -2409,7 +2409,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.services.schema_service.pagers.ListSchemaRevisionsPager", @@ -2490,7 +2490,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.services.schema_service.pagers.ListSchemasAsyncPager", @@ -2570,7 +2570,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.services.schema_service.pagers.ListSchemasPager", @@ -2655,7 +2655,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Schema", @@ -2739,7 +2739,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Schema", @@ -2816,7 +2816,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.ValidateMessageResponse", @@ -2892,7 +2892,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.ValidateMessageResponse", @@ -2977,7 +2977,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.ValidateSchemaResponse", @@ -3061,7 +3061,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.ValidateSchemaResponse", @@ -3146,7 +3146,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "shortName": "acknowledge" @@ -3227,7 +3227,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "shortName": "acknowledge" @@ -3309,7 +3309,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Snapshot", @@ -3393,7 +3393,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Snapshot", @@ -3486,7 +3486,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Subscription", @@ -3578,7 +3578,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Subscription", @@ -3659,7 +3659,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "shortName": "delete_snapshot" @@ -3736,7 +3736,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "shortName": "delete_snapshot" @@ -3814,7 +3814,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "shortName": "delete_subscription" @@ -3891,7 +3891,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "shortName": "delete_subscription" @@ -3969,7 +3969,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Snapshot", @@ -4049,7 +4049,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Snapshot", @@ -4130,7 +4130,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Subscription", @@ -4210,7 +4210,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Subscription", @@ -4291,7 +4291,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.services.subscriber.pagers.ListSnapshotsAsyncPager", @@ -4371,7 +4371,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.services.subscriber.pagers.ListSnapshotsPager", @@ -4452,7 +4452,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.services.subscriber.pagers.ListSubscriptionsAsyncPager", @@ -4532,7 +4532,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.services.subscriber.pagers.ListSubscriptionsPager", @@ -4621,7 +4621,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "shortName": "modify_ack_deadline" @@ -4706,7 +4706,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "shortName": "modify_ack_deadline" @@ -4788,7 +4788,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "shortName": "modify_push_config" @@ -4869,7 +4869,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "shortName": "modify_push_config" @@ -4955,7 +4955,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.PullResponse", @@ -5043,7 +5043,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.PullResponse", @@ -5120,7 +5120,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.SeekResponse", @@ -5196,7 +5196,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.SeekResponse", @@ -5273,7 +5273,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "Iterable[google.pubsub_v1.types.StreamingPullResponse]", @@ -5349,7 +5349,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "Iterable[google.pubsub_v1.types.StreamingPullResponse]", @@ -5434,7 +5434,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Snapshot", @@ -5518,7 +5518,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Snapshot", @@ -5603,7 +5603,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Subscription", @@ -5687,7 +5687,7 @@ }, { "name": "metadata", - "type": "Sequence[Tuple[str, str]" + "type": "Sequence[Tuple[str, Union[str, bytes]]]" } ], "resultType": "google.pubsub_v1.types.Subscription", diff --git a/scripts/fixup_pubsub_v1_keywords.py b/scripts/fixup_pubsub_v1_keywords.py index afcf7a8b9..2e9609f5f 100644 --- a/scripts/fixup_pubsub_v1_keywords.py +++ b/scripts/fixup_pubsub_v1_keywords.py @@ -74,9 +74,6 @@ class pubsubCallTransformer(cst.CSTTransformer): 'update_topic': ('topic', 'update_mask', ), 'validate_message': ('parent', 'name', 'schema', 'message', 'encoding', ), 'validate_schema': ('parent', 'schema', ), - 'get_iam_policy': ('resource', 'options', ), - 'set_iam_policy': ('resource', 'policy', ), - 'test_iam_permissions': ('resource', 'permissions', ), } def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode: diff --git a/tests/unit/gapic/pubsub_v1/test_publisher.py b/tests/unit/gapic/pubsub_v1/test_publisher.py index 423df4433..4856b1cad 100644 --- a/tests/unit/gapic/pubsub_v1/test_publisher.py +++ b/tests/unit/gapic/pubsub_v1/test_publisher.py @@ -69,6 +69,14 @@ import google.auth +CRED_INFO_JSON = { + "credential_source": "/path/to/file", + "credential_type": "service account credentials", + "principal": "service-account@example.com", +} +CRED_INFO_STRING = json.dumps(CRED_INFO_JSON) + + async def mock_async_gen(data, chunk_size=1): for i in range(0, len(data)): # pragma: NO COVER chunk = data[i : i + chunk_size] @@ -301,6 +309,49 @@ def test__get_universe_domain(): assert str(excinfo.value) == "Universe Domain cannot be an empty string." +@pytest.mark.parametrize( + "error_code,cred_info_json,show_cred_info", + [ + (401, CRED_INFO_JSON, True), + (403, CRED_INFO_JSON, True), + (404, CRED_INFO_JSON, True), + (500, CRED_INFO_JSON, False), + (401, None, False), + (403, None, False), + (404, None, False), + (500, None, False), + ], +) +def test__add_cred_info_for_auth_errors(error_code, cred_info_json, show_cred_info): + cred = mock.Mock(["get_cred_info"]) + cred.get_cred_info = mock.Mock(return_value=cred_info_json) + client = PublisherClient(credentials=cred) + client._transport._credentials = cred + + error = core_exceptions.GoogleAPICallError("message", details=["foo"]) + error.code = error_code + + client._add_cred_info_for_auth_errors(error) + if show_cred_info: + assert error.details == ["foo", CRED_INFO_STRING] + else: + assert error.details == ["foo"] + + +@pytest.mark.parametrize("error_code", [401, 403, 404, 500]) +def test__add_cred_info_for_auth_errors_no_get_cred_info(error_code): + cred = mock.Mock([]) + assert not hasattr(cred, "get_cred_info") + client = PublisherClient(credentials=cred) + client._transport._credentials = cred + + error = core_exceptions.GoogleAPICallError("message", details=[]) + error.code = error_code + + client._add_cred_info_for_auth_errors(error) + assert error.details == [] + + @pytest.mark.parametrize( "client_class,transport_name", [ @@ -4639,6 +4690,7 @@ def test_create_topic_rest_required_fields(request_type=pubsub.Topic): response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.create_topic(request) @@ -4684,6 +4736,7 @@ def test_create_topic_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.create_topic(**mock_args) @@ -4807,6 +4860,7 @@ def test_update_topic_rest_required_fields(request_type=pubsub.UpdateTopicReques response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.update_topic(request) @@ -4861,6 +4915,7 @@ def test_update_topic_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.update_topic(**mock_args) @@ -4990,6 +5045,7 @@ def test_publish_rest_required_fields(request_type=pubsub.PublishRequest): response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.publish(request) @@ -5044,6 +5100,7 @@ def test_publish_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.publish(**mock_args) @@ -5173,6 +5230,7 @@ def test_get_topic_rest_required_fields(request_type=pubsub.GetTopicRequest): response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.get_topic(request) @@ -5218,6 +5276,7 @@ def test_get_topic_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.get_topic(**mock_args) @@ -5352,6 +5411,7 @@ def test_list_topics_rest_required_fields(request_type=pubsub.ListTopicsRequest) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.list_topics(request) @@ -5405,6 +5465,7 @@ def test_list_topics_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.list_topics(**mock_args) @@ -5607,6 +5668,7 @@ def test_list_topic_subscriptions_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.list_topic_subscriptions(request) @@ -5660,6 +5722,7 @@ def test_list_topic_subscriptions_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.list_topic_subscriptions(**mock_args) @@ -5864,6 +5927,7 @@ def test_list_topic_snapshots_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.list_topic_snapshots(request) @@ -5917,6 +5981,7 @@ def test_list_topic_snapshots_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.list_topic_snapshots(**mock_args) @@ -6103,6 +6168,7 @@ def test_delete_topic_rest_required_fields(request_type=pubsub.DeleteTopicReques response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.delete_topic(request) @@ -6146,6 +6212,7 @@ def test_delete_topic_rest_flattened(): json_return_value = "" response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.delete_topic(**mock_args) @@ -6279,6 +6346,7 @@ def test_detach_subscription_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.detach_subscription(request) @@ -6891,6 +6959,7 @@ def test_create_topic_rest_bad_request(request_type=pubsub.Topic): response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.create_topic(request) @@ -6929,6 +6998,7 @@ def test_create_topic_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.create_topic(request) # Establish that the response is the type that we expect. @@ -6954,10 +7024,13 @@ def test_create_topic_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.PublisherRestInterceptor, "post_create_topic" ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "post_create_topic_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.PublisherRestInterceptor, "pre_create_topic" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.Topic.pb(pubsub.Topic()) transcode.return_value = { "method": "post", @@ -6968,6 +7041,7 @@ def test_create_topic_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.Topic.to_json(pubsub.Topic()) req.return_value.content = return_value @@ -6978,6 +7052,7 @@ def test_create_topic_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.Topic() + post_with_metadata.return_value = pubsub.Topic(), metadata client.create_topic( request, @@ -6989,6 +7064,7 @@ def test_create_topic_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_update_topic_rest_bad_request(request_type=pubsub.UpdateTopicRequest): @@ -7010,6 +7086,7 @@ def test_update_topic_rest_bad_request(request_type=pubsub.UpdateTopicRequest): response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.update_topic(request) @@ -7048,6 +7125,7 @@ def test_update_topic_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.update_topic(request) # Establish that the response is the type that we expect. @@ -7073,10 +7151,13 @@ def test_update_topic_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.PublisherRestInterceptor, "post_update_topic" ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "post_update_topic_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.PublisherRestInterceptor, "pre_update_topic" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.UpdateTopicRequest.pb(pubsub.UpdateTopicRequest()) transcode.return_value = { "method": "post", @@ -7087,6 +7168,7 @@ def test_update_topic_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.Topic.to_json(pubsub.Topic()) req.return_value.content = return_value @@ -7097,6 +7179,7 @@ def test_update_topic_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.Topic() + post_with_metadata.return_value = pubsub.Topic(), metadata client.update_topic( request, @@ -7108,6 +7191,7 @@ def test_update_topic_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_publish_rest_bad_request(request_type=pubsub.PublishRequest): @@ -7129,6 +7213,7 @@ def test_publish_rest_bad_request(request_type=pubsub.PublishRequest): response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.publish(request) @@ -7164,6 +7249,7 @@ def test_publish_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.publish(request) # Establish that the response is the type that we expect. @@ -7186,10 +7272,13 @@ def test_publish_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.PublisherRestInterceptor, "post_publish" ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "post_publish_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.PublisherRestInterceptor, "pre_publish" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.PublishRequest.pb(pubsub.PublishRequest()) transcode.return_value = { "method": "post", @@ -7200,6 +7289,7 @@ def test_publish_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.PublishResponse.to_json(pubsub.PublishResponse()) req.return_value.content = return_value @@ -7210,6 +7300,7 @@ def test_publish_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.PublishResponse() + post_with_metadata.return_value = pubsub.PublishResponse(), metadata client.publish( request, @@ -7221,6 +7312,7 @@ def test_publish_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_get_topic_rest_bad_request(request_type=pubsub.GetTopicRequest): @@ -7242,6 +7334,7 @@ def test_get_topic_rest_bad_request(request_type=pubsub.GetTopicRequest): response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.get_topic(request) @@ -7280,6 +7373,7 @@ def test_get_topic_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.get_topic(request) # Establish that the response is the type that we expect. @@ -7305,10 +7399,13 @@ def test_get_topic_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.PublisherRestInterceptor, "post_get_topic" ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "post_get_topic_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.PublisherRestInterceptor, "pre_get_topic" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.GetTopicRequest.pb(pubsub.GetTopicRequest()) transcode.return_value = { "method": "post", @@ -7319,6 +7416,7 @@ def test_get_topic_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.Topic.to_json(pubsub.Topic()) req.return_value.content = return_value @@ -7329,6 +7427,7 @@ def test_get_topic_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.Topic() + post_with_metadata.return_value = pubsub.Topic(), metadata client.get_topic( request, @@ -7340,6 +7439,7 @@ def test_get_topic_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_list_topics_rest_bad_request(request_type=pubsub.ListTopicsRequest): @@ -7361,6 +7461,7 @@ def test_list_topics_rest_bad_request(request_type=pubsub.ListTopicsRequest): response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.list_topics(request) @@ -7396,6 +7497,7 @@ def test_list_topics_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.list_topics(request) # Establish that the response is the type that we expect. @@ -7418,10 +7520,13 @@ def test_list_topics_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.PublisherRestInterceptor, "post_list_topics" ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "post_list_topics_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.PublisherRestInterceptor, "pre_list_topics" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.ListTopicsRequest.pb(pubsub.ListTopicsRequest()) transcode.return_value = { "method": "post", @@ -7432,6 +7537,7 @@ def test_list_topics_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.ListTopicsResponse.to_json(pubsub.ListTopicsResponse()) req.return_value.content = return_value @@ -7442,6 +7548,7 @@ def test_list_topics_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.ListTopicsResponse() + post_with_metadata.return_value = pubsub.ListTopicsResponse(), metadata client.list_topics( request, @@ -7453,6 +7560,7 @@ def test_list_topics_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_list_topic_subscriptions_rest_bad_request( @@ -7476,6 +7584,7 @@ def test_list_topic_subscriptions_rest_bad_request( response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.list_topic_subscriptions(request) @@ -7512,6 +7621,7 @@ def test_list_topic_subscriptions_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.list_topic_subscriptions(request) # Establish that the response is the type that we expect. @@ -7535,10 +7645,14 @@ def test_list_topic_subscriptions_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.PublisherRestInterceptor, "post_list_topic_subscriptions" ) as post, mock.patch.object( + transports.PublisherRestInterceptor, + "post_list_topic_subscriptions_with_metadata", + ) as post_with_metadata, mock.patch.object( transports.PublisherRestInterceptor, "pre_list_topic_subscriptions" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.ListTopicSubscriptionsRequest.pb( pubsub.ListTopicSubscriptionsRequest() ) @@ -7551,6 +7665,7 @@ def test_list_topic_subscriptions_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.ListTopicSubscriptionsResponse.to_json( pubsub.ListTopicSubscriptionsResponse() ) @@ -7563,6 +7678,10 @@ def test_list_topic_subscriptions_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.ListTopicSubscriptionsResponse() + post_with_metadata.return_value = ( + pubsub.ListTopicSubscriptionsResponse(), + metadata, + ) client.list_topic_subscriptions( request, @@ -7574,6 +7693,7 @@ def test_list_topic_subscriptions_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_list_topic_snapshots_rest_bad_request( @@ -7597,6 +7717,7 @@ def test_list_topic_snapshots_rest_bad_request( response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.list_topic_snapshots(request) @@ -7633,6 +7754,7 @@ def test_list_topic_snapshots_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.list_topic_snapshots(request) # Establish that the response is the type that we expect. @@ -7656,10 +7778,13 @@ def test_list_topic_snapshots_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.PublisherRestInterceptor, "post_list_topic_snapshots" ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "post_list_topic_snapshots_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.PublisherRestInterceptor, "pre_list_topic_snapshots" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.ListTopicSnapshotsRequest.pb( pubsub.ListTopicSnapshotsRequest() ) @@ -7672,6 +7797,7 @@ def test_list_topic_snapshots_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.ListTopicSnapshotsResponse.to_json( pubsub.ListTopicSnapshotsResponse() ) @@ -7684,6 +7810,7 @@ def test_list_topic_snapshots_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.ListTopicSnapshotsResponse() + post_with_metadata.return_value = pubsub.ListTopicSnapshotsResponse(), metadata client.list_topic_snapshots( request, @@ -7695,6 +7822,7 @@ def test_list_topic_snapshots_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_delete_topic_rest_bad_request(request_type=pubsub.DeleteTopicRequest): @@ -7716,6 +7844,7 @@ def test_delete_topic_rest_bad_request(request_type=pubsub.DeleteTopicRequest): response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.delete_topic(request) @@ -7746,6 +7875,7 @@ def test_delete_topic_rest_call_success(request_type): json_return_value = "" response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.delete_topic(request) # Establish that the response is the type that we expect. @@ -7778,6 +7908,7 @@ def test_delete_topic_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} request = pubsub.DeleteTopicRequest() metadata = [ @@ -7818,6 +7949,7 @@ def test_detach_subscription_rest_bad_request( response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.detach_subscription(request) @@ -7851,6 +7983,7 @@ def test_detach_subscription_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.detach_subscription(request) # Establish that the response is the type that we expect. @@ -7872,10 +8005,13 @@ def test_detach_subscription_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.PublisherRestInterceptor, "post_detach_subscription" ) as post, mock.patch.object( + transports.PublisherRestInterceptor, "post_detach_subscription_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.PublisherRestInterceptor, "pre_detach_subscription" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.DetachSubscriptionRequest.pb( pubsub.DetachSubscriptionRequest() ) @@ -7888,6 +8024,7 @@ def test_detach_subscription_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.DetachSubscriptionResponse.to_json( pubsub.DetachSubscriptionResponse() ) @@ -7900,6 +8037,7 @@ def test_detach_subscription_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.DetachSubscriptionResponse() + post_with_metadata.return_value = pubsub.DetachSubscriptionResponse(), metadata client.detach_subscription( request, @@ -7911,6 +8049,7 @@ def test_detach_subscription_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_get_iam_policy_rest_bad_request( @@ -7936,6 +8075,7 @@ def test_get_iam_policy_rest_bad_request( response_value.status_code = 400 response_value.request = Request() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.get_iam_policy(request) @@ -7966,6 +8106,7 @@ def test_get_iam_policy_rest(request_type): response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.get_iam_policy(request) @@ -7996,6 +8137,7 @@ def test_set_iam_policy_rest_bad_request( response_value.status_code = 400 response_value.request = Request() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.set_iam_policy(request) @@ -8026,6 +8168,7 @@ def test_set_iam_policy_rest(request_type): response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.set_iam_policy(request) @@ -8056,6 +8199,7 @@ def test_test_iam_permissions_rest_bad_request( response_value.status_code = 400 response_value.request = Request() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.test_iam_permissions(request) @@ -8086,6 +8230,7 @@ def test_test_iam_permissions_rest(request_type): response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.test_iam_permissions(request) diff --git a/tests/unit/gapic/pubsub_v1/test_schema_service.py b/tests/unit/gapic/pubsub_v1/test_schema_service.py index 3dbefc470..194cddacf 100644 --- a/tests/unit/gapic/pubsub_v1/test_schema_service.py +++ b/tests/unit/gapic/pubsub_v1/test_schema_service.py @@ -67,6 +67,14 @@ import google.auth +CRED_INFO_JSON = { + "credential_source": "/path/to/file", + "credential_type": "service account credentials", + "principal": "service-account@example.com", +} +CRED_INFO_STRING = json.dumps(CRED_INFO_JSON) + + async def mock_async_gen(data, chunk_size=1): for i in range(0, len(data)): # pragma: NO COVER chunk = data[i : i + chunk_size] @@ -320,6 +328,49 @@ def test__get_universe_domain(): assert str(excinfo.value) == "Universe Domain cannot be an empty string." +@pytest.mark.parametrize( + "error_code,cred_info_json,show_cred_info", + [ + (401, CRED_INFO_JSON, True), + (403, CRED_INFO_JSON, True), + (404, CRED_INFO_JSON, True), + (500, CRED_INFO_JSON, False), + (401, None, False), + (403, None, False), + (404, None, False), + (500, None, False), + ], +) +def test__add_cred_info_for_auth_errors(error_code, cred_info_json, show_cred_info): + cred = mock.Mock(["get_cred_info"]) + cred.get_cred_info = mock.Mock(return_value=cred_info_json) + client = SchemaServiceClient(credentials=cred) + client._transport._credentials = cred + + error = core_exceptions.GoogleAPICallError("message", details=["foo"]) + error.code = error_code + + client._add_cred_info_for_auth_errors(error) + if show_cred_info: + assert error.details == ["foo", CRED_INFO_STRING] + else: + assert error.details == ["foo"] + + +@pytest.mark.parametrize("error_code", [401, 403, 404, 500]) +def test__add_cred_info_for_auth_errors_no_get_cred_info(error_code): + cred = mock.Mock([]) + assert not hasattr(cred, "get_cred_info") + client = SchemaServiceClient(credentials=cred) + client._transport._credentials = cred + + error = core_exceptions.GoogleAPICallError("message", details=[]) + error.code = error_code + + client._add_cred_info_for_auth_errors(error) + assert error.details == [] + + @pytest.mark.parametrize( "client_class,transport_name", [ @@ -4840,6 +4891,7 @@ def test_create_schema_rest_required_fields(request_type=gp_schema.CreateSchemaR response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.create_schema(request) @@ -4895,6 +4947,7 @@ def test_create_schema_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.create_schema(**mock_args) @@ -5026,6 +5079,7 @@ def test_get_schema_rest_required_fields(request_type=schema.GetSchemaRequest): response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.get_schema(request) @@ -5071,6 +5125,7 @@ def test_get_schema_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.get_schema(**mock_args) @@ -5206,6 +5261,7 @@ def test_list_schemas_rest_required_fields(request_type=schema.ListSchemasReques response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.list_schemas(request) @@ -5260,6 +5316,7 @@ def test_list_schemas_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.list_schemas(**mock_args) @@ -5463,6 +5520,7 @@ def test_list_schema_revisions_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.list_schema_revisions(request) @@ -5517,6 +5575,7 @@ def test_list_schema_revisions_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.list_schema_revisions(**mock_args) @@ -5709,6 +5768,7 @@ def test_commit_schema_rest_required_fields(request_type=gp_schema.CommitSchemaR response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.commit_schema(request) @@ -5763,6 +5823,7 @@ def test_commit_schema_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.commit_schema(**mock_args) @@ -5898,6 +5959,7 @@ def test_rollback_schema_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.rollback_schema(request) @@ -5952,6 +6014,7 @@ def test_rollback_schema_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.rollback_schema(**mock_args) @@ -6090,6 +6153,7 @@ def test_delete_schema_revision_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.delete_schema_revision(request) @@ -6136,6 +6200,7 @@ def test_delete_schema_revision_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.delete_schema_revision(**mock_args) @@ -6262,6 +6327,7 @@ def test_delete_schema_rest_required_fields(request_type=schema.DeleteSchemaRequ response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.delete_schema(request) @@ -6305,6 +6371,7 @@ def test_delete_schema_rest_flattened(): json_return_value = "" response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.delete_schema(**mock_args) @@ -6435,6 +6502,7 @@ def test_validate_schema_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.validate_schema(request) @@ -6489,6 +6557,7 @@ def test_validate_schema_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.validate_schema(**mock_args) @@ -6623,6 +6692,7 @@ def test_validate_message_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.validate_message(request) @@ -7281,6 +7351,7 @@ def test_create_schema_rest_bad_request(request_type=gp_schema.CreateSchemaReque response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.create_schema(request) @@ -7393,6 +7464,7 @@ def get_message_fields(field): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.create_schema(request) # Establish that the response is the type that we expect. @@ -7420,10 +7492,13 @@ def test_create_schema_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SchemaServiceRestInterceptor, "post_create_schema" ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_create_schema_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SchemaServiceRestInterceptor, "pre_create_schema" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = gp_schema.CreateSchemaRequest.pb(gp_schema.CreateSchemaRequest()) transcode.return_value = { "method": "post", @@ -7434,6 +7509,7 @@ def test_create_schema_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = gp_schema.Schema.to_json(gp_schema.Schema()) req.return_value.content = return_value @@ -7444,6 +7520,7 @@ def test_create_schema_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = gp_schema.Schema() + post_with_metadata.return_value = gp_schema.Schema(), metadata client.create_schema( request, @@ -7455,6 +7532,7 @@ def test_create_schema_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_get_schema_rest_bad_request(request_type=schema.GetSchemaRequest): @@ -7476,6 +7554,7 @@ def test_get_schema_rest_bad_request(request_type=schema.GetSchemaRequest): response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.get_schema(request) @@ -7514,6 +7593,7 @@ def test_get_schema_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.get_schema(request) # Establish that the response is the type that we expect. @@ -7541,10 +7621,13 @@ def test_get_schema_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SchemaServiceRestInterceptor, "post_get_schema" ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_get_schema_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SchemaServiceRestInterceptor, "pre_get_schema" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = schema.GetSchemaRequest.pb(schema.GetSchemaRequest()) transcode.return_value = { "method": "post", @@ -7555,6 +7638,7 @@ def test_get_schema_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = schema.Schema.to_json(schema.Schema()) req.return_value.content = return_value @@ -7565,6 +7649,7 @@ def test_get_schema_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = schema.Schema() + post_with_metadata.return_value = schema.Schema(), metadata client.get_schema( request, @@ -7576,6 +7661,7 @@ def test_get_schema_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_list_schemas_rest_bad_request(request_type=schema.ListSchemasRequest): @@ -7597,6 +7683,7 @@ def test_list_schemas_rest_bad_request(request_type=schema.ListSchemasRequest): response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.list_schemas(request) @@ -7632,6 +7719,7 @@ def test_list_schemas_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.list_schemas(request) # Establish that the response is the type that we expect. @@ -7656,10 +7744,13 @@ def test_list_schemas_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SchemaServiceRestInterceptor, "post_list_schemas" ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_list_schemas_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SchemaServiceRestInterceptor, "pre_list_schemas" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = schema.ListSchemasRequest.pb(schema.ListSchemasRequest()) transcode.return_value = { "method": "post", @@ -7670,6 +7761,7 @@ def test_list_schemas_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = schema.ListSchemasResponse.to_json(schema.ListSchemasResponse()) req.return_value.content = return_value @@ -7680,6 +7772,7 @@ def test_list_schemas_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = schema.ListSchemasResponse() + post_with_metadata.return_value = schema.ListSchemasResponse(), metadata client.list_schemas( request, @@ -7691,6 +7784,7 @@ def test_list_schemas_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_list_schema_revisions_rest_bad_request( @@ -7714,6 +7808,7 @@ def test_list_schema_revisions_rest_bad_request( response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.list_schema_revisions(request) @@ -7749,6 +7844,7 @@ def test_list_schema_revisions_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.list_schema_revisions(request) # Establish that the response is the type that we expect. @@ -7773,10 +7869,14 @@ def test_list_schema_revisions_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SchemaServiceRestInterceptor, "post_list_schema_revisions" ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, + "post_list_schema_revisions_with_metadata", + ) as post_with_metadata, mock.patch.object( transports.SchemaServiceRestInterceptor, "pre_list_schema_revisions" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = schema.ListSchemaRevisionsRequest.pb( schema.ListSchemaRevisionsRequest() ) @@ -7789,6 +7889,7 @@ def test_list_schema_revisions_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = schema.ListSchemaRevisionsResponse.to_json( schema.ListSchemaRevisionsResponse() ) @@ -7801,6 +7902,7 @@ def test_list_schema_revisions_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = schema.ListSchemaRevisionsResponse() + post_with_metadata.return_value = schema.ListSchemaRevisionsResponse(), metadata client.list_schema_revisions( request, @@ -7812,6 +7914,7 @@ def test_list_schema_revisions_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_commit_schema_rest_bad_request(request_type=gp_schema.CommitSchemaRequest): @@ -7833,6 +7936,7 @@ def test_commit_schema_rest_bad_request(request_type=gp_schema.CommitSchemaReque response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.commit_schema(request) @@ -7871,6 +7975,7 @@ def test_commit_schema_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.commit_schema(request) # Establish that the response is the type that we expect. @@ -7898,10 +8003,13 @@ def test_commit_schema_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SchemaServiceRestInterceptor, "post_commit_schema" ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_commit_schema_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SchemaServiceRestInterceptor, "pre_commit_schema" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = gp_schema.CommitSchemaRequest.pb(gp_schema.CommitSchemaRequest()) transcode.return_value = { "method": "post", @@ -7912,6 +8020,7 @@ def test_commit_schema_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = gp_schema.Schema.to_json(gp_schema.Schema()) req.return_value.content = return_value @@ -7922,6 +8031,7 @@ def test_commit_schema_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = gp_schema.Schema() + post_with_metadata.return_value = gp_schema.Schema(), metadata client.commit_schema( request, @@ -7933,6 +8043,7 @@ def test_commit_schema_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_rollback_schema_rest_bad_request(request_type=schema.RollbackSchemaRequest): @@ -7954,6 +8065,7 @@ def test_rollback_schema_rest_bad_request(request_type=schema.RollbackSchemaRequ response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.rollback_schema(request) @@ -7992,6 +8104,7 @@ def test_rollback_schema_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.rollback_schema(request) # Establish that the response is the type that we expect. @@ -8019,10 +8132,13 @@ def test_rollback_schema_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SchemaServiceRestInterceptor, "post_rollback_schema" ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_rollback_schema_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SchemaServiceRestInterceptor, "pre_rollback_schema" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = schema.RollbackSchemaRequest.pb(schema.RollbackSchemaRequest()) transcode.return_value = { "method": "post", @@ -8033,6 +8149,7 @@ def test_rollback_schema_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = schema.Schema.to_json(schema.Schema()) req.return_value.content = return_value @@ -8043,6 +8160,7 @@ def test_rollback_schema_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = schema.Schema() + post_with_metadata.return_value = schema.Schema(), metadata client.rollback_schema( request, @@ -8054,6 +8172,7 @@ def test_rollback_schema_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_delete_schema_revision_rest_bad_request( @@ -8077,6 +8196,7 @@ def test_delete_schema_revision_rest_bad_request( response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.delete_schema_revision(request) @@ -8115,6 +8235,7 @@ def test_delete_schema_revision_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.delete_schema_revision(request) # Establish that the response is the type that we expect. @@ -8142,10 +8263,14 @@ def test_delete_schema_revision_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SchemaServiceRestInterceptor, "post_delete_schema_revision" ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, + "post_delete_schema_revision_with_metadata", + ) as post_with_metadata, mock.patch.object( transports.SchemaServiceRestInterceptor, "pre_delete_schema_revision" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = schema.DeleteSchemaRevisionRequest.pb( schema.DeleteSchemaRevisionRequest() ) @@ -8158,6 +8283,7 @@ def test_delete_schema_revision_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = schema.Schema.to_json(schema.Schema()) req.return_value.content = return_value @@ -8168,6 +8294,7 @@ def test_delete_schema_revision_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = schema.Schema() + post_with_metadata.return_value = schema.Schema(), metadata client.delete_schema_revision( request, @@ -8179,6 +8306,7 @@ def test_delete_schema_revision_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_delete_schema_rest_bad_request(request_type=schema.DeleteSchemaRequest): @@ -8200,6 +8328,7 @@ def test_delete_schema_rest_bad_request(request_type=schema.DeleteSchemaRequest) response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.delete_schema(request) @@ -8230,6 +8359,7 @@ def test_delete_schema_rest_call_success(request_type): json_return_value = "" response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.delete_schema(request) # Establish that the response is the type that we expect. @@ -8264,6 +8394,7 @@ def test_delete_schema_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} request = schema.DeleteSchemaRequest() metadata = [ @@ -8302,6 +8433,7 @@ def test_validate_schema_rest_bad_request(request_type=gp_schema.ValidateSchemaR response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.validate_schema(request) @@ -8335,6 +8467,7 @@ def test_validate_schema_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.validate_schema(request) # Establish that the response is the type that we expect. @@ -8358,10 +8491,13 @@ def test_validate_schema_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SchemaServiceRestInterceptor, "post_validate_schema" ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_validate_schema_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SchemaServiceRestInterceptor, "pre_validate_schema" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = gp_schema.ValidateSchemaRequest.pb( gp_schema.ValidateSchemaRequest() ) @@ -8374,6 +8510,7 @@ def test_validate_schema_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = gp_schema.ValidateSchemaResponse.to_json( gp_schema.ValidateSchemaResponse() ) @@ -8386,6 +8523,7 @@ def test_validate_schema_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = gp_schema.ValidateSchemaResponse() + post_with_metadata.return_value = gp_schema.ValidateSchemaResponse(), metadata client.validate_schema( request, @@ -8397,6 +8535,7 @@ def test_validate_schema_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_validate_message_rest_bad_request(request_type=schema.ValidateMessageRequest): @@ -8418,6 +8557,7 @@ def test_validate_message_rest_bad_request(request_type=schema.ValidateMessageRe response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.validate_message(request) @@ -8451,6 +8591,7 @@ def test_validate_message_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.validate_message(request) # Establish that the response is the type that we expect. @@ -8474,10 +8615,13 @@ def test_validate_message_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SchemaServiceRestInterceptor, "post_validate_message" ) as post, mock.patch.object( + transports.SchemaServiceRestInterceptor, "post_validate_message_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SchemaServiceRestInterceptor, "pre_validate_message" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = schema.ValidateMessageRequest.pb(schema.ValidateMessageRequest()) transcode.return_value = { "method": "post", @@ -8488,6 +8632,7 @@ def test_validate_message_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = schema.ValidateMessageResponse.to_json( schema.ValidateMessageResponse() ) @@ -8500,6 +8645,7 @@ def test_validate_message_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = schema.ValidateMessageResponse() + post_with_metadata.return_value = schema.ValidateMessageResponse(), metadata client.validate_message( request, @@ -8511,6 +8657,7 @@ def test_validate_message_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_get_iam_policy_rest_bad_request( @@ -8536,6 +8683,7 @@ def test_get_iam_policy_rest_bad_request( response_value.status_code = 400 response_value.request = Request() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.get_iam_policy(request) @@ -8566,6 +8714,7 @@ def test_get_iam_policy_rest(request_type): response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.get_iam_policy(request) @@ -8596,6 +8745,7 @@ def test_set_iam_policy_rest_bad_request( response_value.status_code = 400 response_value.request = Request() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.set_iam_policy(request) @@ -8626,6 +8776,7 @@ def test_set_iam_policy_rest(request_type): response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.set_iam_policy(request) @@ -8656,6 +8807,7 @@ def test_test_iam_permissions_rest_bad_request( response_value.status_code = 400 response_value.request = Request() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.test_iam_permissions(request) @@ -8686,6 +8838,7 @@ def test_test_iam_permissions_rest(request_type): response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.test_iam_permissions(request) diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py index 4478942f7..966e30957 100644 --- a/tests/unit/gapic/pubsub_v1/test_subscriber.py +++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py @@ -69,6 +69,14 @@ import google.auth +CRED_INFO_JSON = { + "credential_source": "/path/to/file", + "credential_type": "service account credentials", + "principal": "service-account@example.com", +} +CRED_INFO_STRING = json.dumps(CRED_INFO_JSON) + + async def mock_async_gen(data, chunk_size=1): for i in range(0, len(data)): # pragma: NO COVER chunk = data[i : i + chunk_size] @@ -303,6 +311,49 @@ def test__get_universe_domain(): assert str(excinfo.value) == "Universe Domain cannot be an empty string." +@pytest.mark.parametrize( + "error_code,cred_info_json,show_cred_info", + [ + (401, CRED_INFO_JSON, True), + (403, CRED_INFO_JSON, True), + (404, CRED_INFO_JSON, True), + (500, CRED_INFO_JSON, False), + (401, None, False), + (403, None, False), + (404, None, False), + (500, None, False), + ], +) +def test__add_cred_info_for_auth_errors(error_code, cred_info_json, show_cred_info): + cred = mock.Mock(["get_cred_info"]) + cred.get_cred_info = mock.Mock(return_value=cred_info_json) + client = SubscriberClient(credentials=cred) + client._transport._credentials = cred + + error = core_exceptions.GoogleAPICallError("message", details=["foo"]) + error.code = error_code + + client._add_cred_info_for_auth_errors(error) + if show_cred_info: + assert error.details == ["foo", CRED_INFO_STRING] + else: + assert error.details == ["foo"] + + +@pytest.mark.parametrize("error_code", [401, 403, 404, 500]) +def test__add_cred_info_for_auth_errors_no_get_cred_info(error_code): + cred = mock.Mock([]) + assert not hasattr(cred, "get_cred_info") + client = SubscriberClient(credentials=cred) + client._transport._credentials = cred + + error = core_exceptions.GoogleAPICallError("message", details=[]) + error.code = error_code + + client._add_cred_info_for_auth_errors(error) + assert error.details == [] + + @pytest.mark.parametrize( "client_class,transport_name", [ @@ -6683,6 +6734,7 @@ def test_create_subscription_rest_required_fields(request_type=pubsub.Subscripti response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.create_subscription(request) @@ -6739,6 +6791,7 @@ def test_create_subscription_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.create_subscription(**mock_args) @@ -6873,6 +6926,7 @@ def test_get_subscription_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.get_subscription(request) @@ -6918,6 +6972,7 @@ def test_get_subscription_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.get_subscription(**mock_args) @@ -7048,6 +7103,7 @@ def test_update_subscription_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.update_subscription(request) @@ -7104,6 +7160,7 @@ def test_update_subscription_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.update_subscription(**mock_args) @@ -7247,6 +7304,7 @@ def test_list_subscriptions_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.list_subscriptions(request) @@ -7300,6 +7358,7 @@ def test_list_subscriptions_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.list_subscriptions(**mock_args) @@ -7491,6 +7550,7 @@ def test_delete_subscription_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.delete_subscription(request) @@ -7534,6 +7594,7 @@ def test_delete_subscription_rest_flattened(): json_return_value = "" response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.delete_subscription(**mock_args) @@ -7674,6 +7735,7 @@ def test_modify_ack_deadline_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.modify_ack_deadline(request) @@ -7728,6 +7790,7 @@ def test_modify_ack_deadline_rest_flattened(): json_return_value = "" response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.modify_ack_deadline(**mock_args) @@ -7861,6 +7924,7 @@ def test_acknowledge_rest_required_fields(request_type=pubsub.AcknowledgeRequest response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.acknowledge(request) @@ -7913,6 +7977,7 @@ def test_acknowledge_rest_flattened(): json_return_value = "" response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.acknowledge(**mock_args) @@ -8048,6 +8113,7 @@ def test_pull_rest_required_fields(request_type=pubsub.PullRequest): response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.pull(request) @@ -8103,6 +8169,7 @@ def test_pull_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.pull(**mock_args) @@ -8249,6 +8316,7 @@ def test_modify_push_config_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.modify_push_config(request) @@ -8301,6 +8369,7 @@ def test_modify_push_config_rest_flattened(): json_return_value = "" response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.modify_push_config(**mock_args) @@ -8431,6 +8500,7 @@ def test_get_snapshot_rest_required_fields(request_type=pubsub.GetSnapshotReques response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.get_snapshot(request) @@ -8476,6 +8546,7 @@ def test_get_snapshot_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.get_snapshot(**mock_args) @@ -8610,6 +8681,7 @@ def test_list_snapshots_rest_required_fields(request_type=pubsub.ListSnapshotsRe response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.list_snapshots(request) @@ -8663,6 +8735,7 @@ def test_list_snapshots_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.list_snapshots(**mock_args) @@ -8858,6 +8931,7 @@ def test_create_snapshot_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.create_snapshot(request) @@ -8912,6 +8986,7 @@ def test_create_snapshot_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.create_snapshot(**mock_args) @@ -9038,6 +9113,7 @@ def test_update_snapshot_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.update_snapshot(request) @@ -9092,6 +9168,7 @@ def test_update_snapshot_rest_flattened(): json_return_value = json_format.MessageToJson(return_value) response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.update_snapshot(**mock_args) @@ -9220,6 +9297,7 @@ def test_delete_snapshot_rest_required_fields( response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.delete_snapshot(request) @@ -9263,6 +9341,7 @@ def test_delete_snapshot_rest_flattened(): json_return_value = "" response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.delete_snapshot(**mock_args) @@ -9391,6 +9470,7 @@ def test_seek_rest_required_fields(request_type=pubsub.SeekRequest): response_value._content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.seek(request) @@ -10310,6 +10390,7 @@ def test_create_subscription_rest_bad_request(request_type=pubsub.Subscription): response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.create_subscription(request) @@ -10353,6 +10434,7 @@ def test_create_subscription_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.create_subscription(request) # Establish that the response is the type that we expect. @@ -10385,10 +10467,13 @@ def test_create_subscription_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SubscriberRestInterceptor, "post_create_subscription" ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "post_create_subscription_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SubscriberRestInterceptor, "pre_create_subscription" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.Subscription.pb(pubsub.Subscription()) transcode.return_value = { "method": "post", @@ -10399,6 +10484,7 @@ def test_create_subscription_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.Subscription.to_json(pubsub.Subscription()) req.return_value.content = return_value @@ -10409,6 +10495,7 @@ def test_create_subscription_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.Subscription() + post_with_metadata.return_value = pubsub.Subscription(), metadata client.create_subscription( request, @@ -10420,6 +10507,7 @@ def test_create_subscription_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_get_subscription_rest_bad_request(request_type=pubsub.GetSubscriptionRequest): @@ -10441,6 +10529,7 @@ def test_get_subscription_rest_bad_request(request_type=pubsub.GetSubscriptionRe response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.get_subscription(request) @@ -10484,6 +10573,7 @@ def test_get_subscription_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.get_subscription(request) # Establish that the response is the type that we expect. @@ -10516,10 +10606,13 @@ def test_get_subscription_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SubscriberRestInterceptor, "post_get_subscription" ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "post_get_subscription_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SubscriberRestInterceptor, "pre_get_subscription" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.GetSubscriptionRequest.pb(pubsub.GetSubscriptionRequest()) transcode.return_value = { "method": "post", @@ -10530,6 +10623,7 @@ def test_get_subscription_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.Subscription.to_json(pubsub.Subscription()) req.return_value.content = return_value @@ -10540,6 +10634,7 @@ def test_get_subscription_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.Subscription() + post_with_metadata.return_value = pubsub.Subscription(), metadata client.get_subscription( request, @@ -10551,6 +10646,7 @@ def test_get_subscription_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_update_subscription_rest_bad_request( @@ -10574,6 +10670,7 @@ def test_update_subscription_rest_bad_request( response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.update_subscription(request) @@ -10617,6 +10714,7 @@ def test_update_subscription_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.update_subscription(request) # Establish that the response is the type that we expect. @@ -10649,10 +10747,13 @@ def test_update_subscription_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SubscriberRestInterceptor, "post_update_subscription" ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "post_update_subscription_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SubscriberRestInterceptor, "pre_update_subscription" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.UpdateSubscriptionRequest.pb( pubsub.UpdateSubscriptionRequest() ) @@ -10665,6 +10766,7 @@ def test_update_subscription_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.Subscription.to_json(pubsub.Subscription()) req.return_value.content = return_value @@ -10675,6 +10777,7 @@ def test_update_subscription_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.Subscription() + post_with_metadata.return_value = pubsub.Subscription(), metadata client.update_subscription( request, @@ -10686,6 +10789,7 @@ def test_update_subscription_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_list_subscriptions_rest_bad_request( @@ -10709,6 +10813,7 @@ def test_list_subscriptions_rest_bad_request( response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.list_subscriptions(request) @@ -10744,6 +10849,7 @@ def test_list_subscriptions_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.list_subscriptions(request) # Establish that the response is the type that we expect. @@ -10768,10 +10874,13 @@ def test_list_subscriptions_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SubscriberRestInterceptor, "post_list_subscriptions" ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "post_list_subscriptions_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SubscriberRestInterceptor, "pre_list_subscriptions" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.ListSubscriptionsRequest.pb( pubsub.ListSubscriptionsRequest() ) @@ -10784,6 +10893,7 @@ def test_list_subscriptions_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.ListSubscriptionsResponse.to_json( pubsub.ListSubscriptionsResponse() ) @@ -10796,6 +10906,7 @@ def test_list_subscriptions_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.ListSubscriptionsResponse() + post_with_metadata.return_value = pubsub.ListSubscriptionsResponse(), metadata client.list_subscriptions( request, @@ -10807,6 +10918,7 @@ def test_list_subscriptions_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_delete_subscription_rest_bad_request( @@ -10830,6 +10942,7 @@ def test_delete_subscription_rest_bad_request( response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.delete_subscription(request) @@ -10860,6 +10973,7 @@ def test_delete_subscription_rest_call_success(request_type): json_return_value = "" response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.delete_subscription(request) # Establish that the response is the type that we expect. @@ -10896,6 +11010,7 @@ def test_delete_subscription_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} request = pubsub.DeleteSubscriptionRequest() metadata = [ @@ -10936,6 +11051,7 @@ def test_modify_ack_deadline_rest_bad_request( response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.modify_ack_deadline(request) @@ -10966,6 +11082,7 @@ def test_modify_ack_deadline_rest_call_success(request_type): json_return_value = "" response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.modify_ack_deadline(request) # Establish that the response is the type that we expect. @@ -11002,6 +11119,7 @@ def test_modify_ack_deadline_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} request = pubsub.ModifyAckDeadlineRequest() metadata = [ @@ -11040,6 +11158,7 @@ def test_acknowledge_rest_bad_request(request_type=pubsub.AcknowledgeRequest): response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.acknowledge(request) @@ -11070,6 +11189,7 @@ def test_acknowledge_rest_call_success(request_type): json_return_value = "" response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.acknowledge(request) # Establish that the response is the type that we expect. @@ -11104,6 +11224,7 @@ def test_acknowledge_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} request = pubsub.AcknowledgeRequest() metadata = [ @@ -11142,6 +11263,7 @@ def test_pull_rest_bad_request(request_type=pubsub.PullRequest): response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.pull(request) @@ -11175,6 +11297,7 @@ def test_pull_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.pull(request) # Establish that the response is the type that we expect. @@ -11198,10 +11321,13 @@ def test_pull_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SubscriberRestInterceptor, "post_pull" ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "post_pull_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SubscriberRestInterceptor, "pre_pull" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.PullRequest.pb(pubsub.PullRequest()) transcode.return_value = { "method": "post", @@ -11212,6 +11338,7 @@ def test_pull_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.PullResponse.to_json(pubsub.PullResponse()) req.return_value.content = return_value @@ -11222,6 +11349,7 @@ def test_pull_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.PullResponse() + post_with_metadata.return_value = pubsub.PullResponse(), metadata client.pull( request, @@ -11233,6 +11361,7 @@ def test_pull_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_streaming_pull_rest_error(): @@ -11268,6 +11397,7 @@ def test_modify_push_config_rest_bad_request( response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.modify_push_config(request) @@ -11298,6 +11428,7 @@ def test_modify_push_config_rest_call_success(request_type): json_return_value = "" response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.modify_push_config(request) # Establish that the response is the type that we expect. @@ -11332,6 +11463,7 @@ def test_modify_push_config_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} request = pubsub.ModifyPushConfigRequest() metadata = [ @@ -11370,6 +11502,7 @@ def test_get_snapshot_rest_bad_request(request_type=pubsub.GetSnapshotRequest): response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.get_snapshot(request) @@ -11406,6 +11539,7 @@ def test_get_snapshot_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.get_snapshot(request) # Establish that the response is the type that we expect. @@ -11431,10 +11565,13 @@ def test_get_snapshot_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SubscriberRestInterceptor, "post_get_snapshot" ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "post_get_snapshot_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SubscriberRestInterceptor, "pre_get_snapshot" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.GetSnapshotRequest.pb(pubsub.GetSnapshotRequest()) transcode.return_value = { "method": "post", @@ -11445,6 +11582,7 @@ def test_get_snapshot_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.Snapshot.to_json(pubsub.Snapshot()) req.return_value.content = return_value @@ -11455,6 +11593,7 @@ def test_get_snapshot_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.Snapshot() + post_with_metadata.return_value = pubsub.Snapshot(), metadata client.get_snapshot( request, @@ -11466,6 +11605,7 @@ def test_get_snapshot_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_list_snapshots_rest_bad_request(request_type=pubsub.ListSnapshotsRequest): @@ -11487,6 +11627,7 @@ def test_list_snapshots_rest_bad_request(request_type=pubsub.ListSnapshotsReques response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.list_snapshots(request) @@ -11522,6 +11663,7 @@ def test_list_snapshots_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.list_snapshots(request) # Establish that the response is the type that we expect. @@ -11546,10 +11688,13 @@ def test_list_snapshots_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SubscriberRestInterceptor, "post_list_snapshots" ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "post_list_snapshots_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SubscriberRestInterceptor, "pre_list_snapshots" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.ListSnapshotsRequest.pb(pubsub.ListSnapshotsRequest()) transcode.return_value = { "method": "post", @@ -11560,6 +11705,7 @@ def test_list_snapshots_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.ListSnapshotsResponse.to_json( pubsub.ListSnapshotsResponse() ) @@ -11572,6 +11718,7 @@ def test_list_snapshots_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.ListSnapshotsResponse() + post_with_metadata.return_value = pubsub.ListSnapshotsResponse(), metadata client.list_snapshots( request, @@ -11583,6 +11730,7 @@ def test_list_snapshots_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_create_snapshot_rest_bad_request(request_type=pubsub.CreateSnapshotRequest): @@ -11604,6 +11752,7 @@ def test_create_snapshot_rest_bad_request(request_type=pubsub.CreateSnapshotRequ response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.create_snapshot(request) @@ -11640,6 +11789,7 @@ def test_create_snapshot_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.create_snapshot(request) # Establish that the response is the type that we expect. @@ -11665,10 +11815,13 @@ def test_create_snapshot_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SubscriberRestInterceptor, "post_create_snapshot" ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "post_create_snapshot_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SubscriberRestInterceptor, "pre_create_snapshot" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.CreateSnapshotRequest.pb(pubsub.CreateSnapshotRequest()) transcode.return_value = { "method": "post", @@ -11679,6 +11832,7 @@ def test_create_snapshot_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.Snapshot.to_json(pubsub.Snapshot()) req.return_value.content = return_value @@ -11689,6 +11843,7 @@ def test_create_snapshot_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.Snapshot() + post_with_metadata.return_value = pubsub.Snapshot(), metadata client.create_snapshot( request, @@ -11700,6 +11855,7 @@ def test_create_snapshot_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_update_snapshot_rest_bad_request(request_type=pubsub.UpdateSnapshotRequest): @@ -11721,6 +11877,7 @@ def test_update_snapshot_rest_bad_request(request_type=pubsub.UpdateSnapshotRequ response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.update_snapshot(request) @@ -11757,6 +11914,7 @@ def test_update_snapshot_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.update_snapshot(request) # Establish that the response is the type that we expect. @@ -11782,10 +11940,13 @@ def test_update_snapshot_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SubscriberRestInterceptor, "post_update_snapshot" ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "post_update_snapshot_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SubscriberRestInterceptor, "pre_update_snapshot" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.UpdateSnapshotRequest.pb(pubsub.UpdateSnapshotRequest()) transcode.return_value = { "method": "post", @@ -11796,6 +11957,7 @@ def test_update_snapshot_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.Snapshot.to_json(pubsub.Snapshot()) req.return_value.content = return_value @@ -11806,6 +11968,7 @@ def test_update_snapshot_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.Snapshot() + post_with_metadata.return_value = pubsub.Snapshot(), metadata client.update_snapshot( request, @@ -11817,6 +11980,7 @@ def test_update_snapshot_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_delete_snapshot_rest_bad_request(request_type=pubsub.DeleteSnapshotRequest): @@ -11838,6 +12002,7 @@ def test_delete_snapshot_rest_bad_request(request_type=pubsub.DeleteSnapshotRequ response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.delete_snapshot(request) @@ -11868,6 +12033,7 @@ def test_delete_snapshot_rest_call_success(request_type): json_return_value = "" response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.delete_snapshot(request) # Establish that the response is the type that we expect. @@ -11902,6 +12068,7 @@ def test_delete_snapshot_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} request = pubsub.DeleteSnapshotRequest() metadata = [ @@ -11940,6 +12107,7 @@ def test_seek_rest_bad_request(request_type=pubsub.SeekRequest): response_value.status_code = 400 response_value.request = mock.Mock() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.seek(request) @@ -11973,6 +12141,7 @@ def test_seek_rest_call_success(request_type): json_return_value = json_format.MessageToJson(return_value) response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.seek(request) # Establish that the response is the type that we expect. @@ -11996,10 +12165,13 @@ def test_seek_rest_interceptors(null_interceptor): ) as transcode, mock.patch.object( transports.SubscriberRestInterceptor, "post_seek" ) as post, mock.patch.object( + transports.SubscriberRestInterceptor, "post_seek_with_metadata" + ) as post_with_metadata, mock.patch.object( transports.SubscriberRestInterceptor, "pre_seek" ) as pre: pre.assert_not_called() post.assert_not_called() + post_with_metadata.assert_not_called() pb_message = pubsub.SeekRequest.pb(pubsub.SeekRequest()) transcode.return_value = { "method": "post", @@ -12010,6 +12182,7 @@ def test_seek_rest_interceptors(null_interceptor): req.return_value = mock.Mock() req.return_value.status_code = 200 + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} return_value = pubsub.SeekResponse.to_json(pubsub.SeekResponse()) req.return_value.content = return_value @@ -12020,6 +12193,7 @@ def test_seek_rest_interceptors(null_interceptor): ] pre.return_value = request, metadata post.return_value = pubsub.SeekResponse() + post_with_metadata.return_value = pubsub.SeekResponse(), metadata client.seek( request, @@ -12031,6 +12205,7 @@ def test_seek_rest_interceptors(null_interceptor): pre.assert_called_once() post.assert_called_once() + post_with_metadata.assert_called_once() def test_get_iam_policy_rest_bad_request( @@ -12056,6 +12231,7 @@ def test_get_iam_policy_rest_bad_request( response_value.status_code = 400 response_value.request = Request() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.get_iam_policy(request) @@ -12086,6 +12262,7 @@ def test_get_iam_policy_rest(request_type): response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.get_iam_policy(request) @@ -12116,6 +12293,7 @@ def test_set_iam_policy_rest_bad_request( response_value.status_code = 400 response_value.request = Request() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.set_iam_policy(request) @@ -12146,6 +12324,7 @@ def test_set_iam_policy_rest(request_type): response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.set_iam_policy(request) @@ -12176,6 +12355,7 @@ def test_test_iam_permissions_rest_bad_request( response_value.status_code = 400 response_value.request = Request() req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} client.test_iam_permissions(request) @@ -12206,6 +12386,7 @@ def test_test_iam_permissions_rest(request_type): response_value.content = json_return_value.encode("UTF-8") req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} response = client.test_iam_permissions(request) From c95b7a5bad7138a70e56c278970f5b54939a68f8 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Mon, 17 Mar 2025 13:11:05 -0400 Subject: [PATCH 141/190] fix: allow Protobuf 6.x (#1369) --- setup.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index 1d68bf87b..899cefde6 100644 --- a/setup.py +++ b/setup.py @@ -36,15 +36,15 @@ release_status = "Development Status :: 5 - Production/Stable" dependencies = [ - "grpcio >= 1.51.3, < 2.0dev", # https://github.com/googleapis/python-pubsub/issues/609 + "grpcio >= 1.51.3, < 2.0.0", # https://github.com/googleapis/python-pubsub/issues/609 # google-api-core >= 1.34.0 is allowed in order to support google-api-core 1.x - "google-auth >= 2.14.1, <3.0.0dev", - "google-api-core[grpc] >= 1.34.0, <3.0.0dev,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,!=2.10.*", - "proto-plus >= 1.22.0, <2.0.0dev", - "proto-plus >= 1.22.2, <2.0.0dev; python_version>='3.11'", - "proto-plus >= 1.25.0, < 2.0.0dev; python_version >= '3.13'", - "protobuf>=3.20.2,<6.0.0dev,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5", - "grpc-google-iam-v1 >= 0.12.4, < 1.0.0dev", + "google-auth >= 2.14.1, <3.0.0", + "google-api-core[grpc] >= 1.34.0, <3.0.0,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,!=2.10.*", + "proto-plus >= 1.22.0, <2.0.0", + "proto-plus >= 1.22.2, <2.0.0; python_version>='3.11'", + "proto-plus >= 1.25.0, < 2.0.0; python_version >= '3.13'", + "protobuf>=3.20.2,<7.0.0,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5", + "grpc-google-iam-v1 >= 0.12.4, < 1.0.0", "grpcio-status >= 1.33.2", "opentelemetry-api <= 1.22.0; python_version<='3.7'", "opentelemetry-api >= 1.27.0; python_version>='3.8'", From e4075da8b7f97132f2d6919a1aaf11c672a0f701 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Wed, 19 Mar 2025 10:53:26 -0400 Subject: [PATCH 142/190] chore: Update gapic-generator-python to 1.23.6 (#1377) Co-authored-by: Owl Bot --- google/pubsub/__init__.py | 2 +- google/pubsub_v1/__init__.py | 2 +- google/pubsub_v1/services/__init__.py | 2 +- google/pubsub_v1/services/publisher/__init__.py | 2 +- google/pubsub_v1/services/publisher/async_client.py | 2 +- google/pubsub_v1/services/publisher/client.py | 2 +- google/pubsub_v1/services/publisher/pagers.py | 2 +- google/pubsub_v1/services/publisher/transports/__init__.py | 2 +- google/pubsub_v1/services/publisher/transports/base.py | 2 +- google/pubsub_v1/services/publisher/transports/grpc.py | 2 +- google/pubsub_v1/services/publisher/transports/grpc_asyncio.py | 2 +- google/pubsub_v1/services/publisher/transports/rest.py | 2 +- google/pubsub_v1/services/publisher/transports/rest_base.py | 2 +- google/pubsub_v1/services/schema_service/__init__.py | 2 +- google/pubsub_v1/services/schema_service/async_client.py | 2 +- google/pubsub_v1/services/schema_service/client.py | 2 +- google/pubsub_v1/services/schema_service/pagers.py | 2 +- google/pubsub_v1/services/schema_service/transports/__init__.py | 2 +- google/pubsub_v1/services/schema_service/transports/base.py | 2 +- google/pubsub_v1/services/schema_service/transports/grpc.py | 2 +- .../services/schema_service/transports/grpc_asyncio.py | 2 +- google/pubsub_v1/services/schema_service/transports/rest.py | 2 +- .../pubsub_v1/services/schema_service/transports/rest_base.py | 2 +- google/pubsub_v1/services/subscriber/__init__.py | 2 +- google/pubsub_v1/services/subscriber/async_client.py | 2 +- google/pubsub_v1/services/subscriber/client.py | 2 +- google/pubsub_v1/services/subscriber/pagers.py | 2 +- google/pubsub_v1/services/subscriber/transports/__init__.py | 2 +- google/pubsub_v1/services/subscriber/transports/base.py | 2 +- google/pubsub_v1/services/subscriber/transports/grpc.py | 2 +- google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py | 2 +- google/pubsub_v1/services/subscriber/transports/rest.py | 2 +- google/pubsub_v1/services/subscriber/transports/rest_base.py | 2 +- google/pubsub_v1/types/__init__.py | 2 +- google/pubsub_v1/types/pubsub.py | 2 +- google/pubsub_v1/types/schema.py | 2 +- .../pubsub_v1_generated_publisher_create_topic_async.py | 2 +- .../pubsub_v1_generated_publisher_create_topic_sync.py | 2 +- .../pubsub_v1_generated_publisher_delete_topic_async.py | 2 +- .../pubsub_v1_generated_publisher_delete_topic_sync.py | 2 +- .../pubsub_v1_generated_publisher_detach_subscription_async.py | 2 +- .../pubsub_v1_generated_publisher_detach_subscription_sync.py | 2 +- .../pubsub_v1_generated_publisher_get_topic_async.py | 2 +- .../pubsub_v1_generated_publisher_get_topic_sync.py | 2 +- .../pubsub_v1_generated_publisher_list_topic_snapshots_async.py | 2 +- .../pubsub_v1_generated_publisher_list_topic_snapshots_sync.py | 2 +- ...sub_v1_generated_publisher_list_topic_subscriptions_async.py | 2 +- ...bsub_v1_generated_publisher_list_topic_subscriptions_sync.py | 2 +- .../pubsub_v1_generated_publisher_list_topics_async.py | 2 +- .../pubsub_v1_generated_publisher_list_topics_sync.py | 2 +- .../pubsub_v1_generated_publisher_publish_async.py | 2 +- .../pubsub_v1_generated_publisher_publish_sync.py | 2 +- .../pubsub_v1_generated_publisher_update_topic_async.py | 2 +- .../pubsub_v1_generated_publisher_update_topic_sync.py | 2 +- .../pubsub_v1_generated_schema_service_commit_schema_async.py | 2 +- .../pubsub_v1_generated_schema_service_commit_schema_sync.py | 2 +- .../pubsub_v1_generated_schema_service_create_schema_async.py | 2 +- .../pubsub_v1_generated_schema_service_create_schema_sync.py | 2 +- .../pubsub_v1_generated_schema_service_delete_schema_async.py | 2 +- ..._v1_generated_schema_service_delete_schema_revision_async.py | 2 +- ...b_v1_generated_schema_service_delete_schema_revision_sync.py | 2 +- .../pubsub_v1_generated_schema_service_delete_schema_sync.py | 2 +- .../pubsub_v1_generated_schema_service_get_schema_async.py | 2 +- .../pubsub_v1_generated_schema_service_get_schema_sync.py | 2 +- ...b_v1_generated_schema_service_list_schema_revisions_async.py | 2 +- ...ub_v1_generated_schema_service_list_schema_revisions_sync.py | 2 +- .../pubsub_v1_generated_schema_service_list_schemas_async.py | 2 +- .../pubsub_v1_generated_schema_service_list_schemas_sync.py | 2 +- .../pubsub_v1_generated_schema_service_rollback_schema_async.py | 2 +- .../pubsub_v1_generated_schema_service_rollback_schema_sync.py | 2 +- ...pubsub_v1_generated_schema_service_validate_message_async.py | 2 +- .../pubsub_v1_generated_schema_service_validate_message_sync.py | 2 +- .../pubsub_v1_generated_schema_service_validate_schema_async.py | 2 +- .../pubsub_v1_generated_schema_service_validate_schema_sync.py | 2 +- .../pubsub_v1_generated_subscriber_acknowledge_async.py | 2 +- .../pubsub_v1_generated_subscriber_acknowledge_sync.py | 2 +- .../pubsub_v1_generated_subscriber_create_snapshot_async.py | 2 +- .../pubsub_v1_generated_subscriber_create_snapshot_sync.py | 2 +- .../pubsub_v1_generated_subscriber_create_subscription_async.py | 2 +- .../pubsub_v1_generated_subscriber_create_subscription_sync.py | 2 +- .../pubsub_v1_generated_subscriber_delete_snapshot_async.py | 2 +- .../pubsub_v1_generated_subscriber_delete_snapshot_sync.py | 2 +- .../pubsub_v1_generated_subscriber_delete_subscription_async.py | 2 +- .../pubsub_v1_generated_subscriber_delete_subscription_sync.py | 2 +- .../pubsub_v1_generated_subscriber_get_snapshot_async.py | 2 +- .../pubsub_v1_generated_subscriber_get_snapshot_sync.py | 2 +- .../pubsub_v1_generated_subscriber_get_subscription_async.py | 2 +- .../pubsub_v1_generated_subscriber_get_subscription_sync.py | 2 +- .../pubsub_v1_generated_subscriber_list_snapshots_async.py | 2 +- .../pubsub_v1_generated_subscriber_list_snapshots_sync.py | 2 +- .../pubsub_v1_generated_subscriber_list_subscriptions_async.py | 2 +- .../pubsub_v1_generated_subscriber_list_subscriptions_sync.py | 2 +- .../pubsub_v1_generated_subscriber_modify_ack_deadline_async.py | 2 +- .../pubsub_v1_generated_subscriber_modify_ack_deadline_sync.py | 2 +- .../pubsub_v1_generated_subscriber_modify_push_config_async.py | 2 +- .../pubsub_v1_generated_subscriber_modify_push_config_sync.py | 2 +- .../pubsub_v1_generated_subscriber_pull_async.py | 2 +- .../pubsub_v1_generated_subscriber_pull_sync.py | 2 +- .../pubsub_v1_generated_subscriber_seek_async.py | 2 +- .../pubsub_v1_generated_subscriber_seek_sync.py | 2 +- .../pubsub_v1_generated_subscriber_streaming_pull_async.py | 2 +- .../pubsub_v1_generated_subscriber_streaming_pull_sync.py | 2 +- .../pubsub_v1_generated_subscriber_update_snapshot_async.py | 2 +- .../pubsub_v1_generated_subscriber_update_snapshot_sync.py | 2 +- .../pubsub_v1_generated_subscriber_update_subscription_async.py | 2 +- .../pubsub_v1_generated_subscriber_update_subscription_sync.py | 2 +- scripts/fixup_pubsub_v1_keywords.py | 2 +- tests/__init__.py | 2 +- tests/unit/__init__.py | 2 +- tests/unit/gapic/__init__.py | 2 +- tests/unit/gapic/pubsub_v1/__init__.py | 2 +- tests/unit/gapic/pubsub_v1/test_publisher.py | 2 +- tests/unit/gapic/pubsub_v1/test_schema_service.py | 2 +- tests/unit/gapic/pubsub_v1/test_subscriber.py | 2 +- 114 files changed, 114 insertions(+), 114 deletions(-) diff --git a/google/pubsub/__init__.py b/google/pubsub/__init__.py index c3f034996..d88449a53 100644 --- a/google/pubsub/__init__.py +++ b/google/pubsub/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/__init__.py b/google/pubsub_v1/__init__.py index 751f77206..9e23f583c 100644 --- a/google/pubsub_v1/__init__.py +++ b/google/pubsub_v1/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/__init__.py b/google/pubsub_v1/services/__init__.py index 8f6cf0682..cbf94b283 100644 --- a/google/pubsub_v1/services/__init__.py +++ b/google/pubsub_v1/services/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/publisher/__init__.py b/google/pubsub_v1/services/publisher/__init__.py index 7e1e8e5ce..6c1355801 100644 --- a/google/pubsub_v1/services/publisher/__init__.py +++ b/google/pubsub_v1/services/publisher/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/publisher/async_client.py b/google/pubsub_v1/services/publisher/async_client.py index 106ce5f93..87838c006 100644 --- a/google/pubsub_v1/services/publisher/async_client.py +++ b/google/pubsub_v1/services/publisher/async_client.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/publisher/client.py b/google/pubsub_v1/services/publisher/client.py index 2db3e1cda..c94cec688 100644 --- a/google/pubsub_v1/services/publisher/client.py +++ b/google/pubsub_v1/services/publisher/client.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/publisher/pagers.py b/google/pubsub_v1/services/publisher/pagers.py index da6de8dfa..162d9da79 100644 --- a/google/pubsub_v1/services/publisher/pagers.py +++ b/google/pubsub_v1/services/publisher/pagers.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/publisher/transports/__init__.py b/google/pubsub_v1/services/publisher/transports/__init__.py index 393b9a55f..75bfa7de0 100644 --- a/google/pubsub_v1/services/publisher/transports/__init__.py +++ b/google/pubsub_v1/services/publisher/transports/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/publisher/transports/base.py b/google/pubsub_v1/services/publisher/transports/base.py index 45b06302d..354a6c714 100644 --- a/google/pubsub_v1/services/publisher/transports/base.py +++ b/google/pubsub_v1/services/publisher/transports/base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/publisher/transports/grpc.py b/google/pubsub_v1/services/publisher/transports/grpc.py index 3b92a0c31..f27b968a8 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc.py +++ b/google/pubsub_v1/services/publisher/transports/grpc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py index 2b6e8c603..90683cbd0 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/publisher/transports/rest.py b/google/pubsub_v1/services/publisher/transports/rest.py index 0089968cc..a5c99da1b 100644 --- a/google/pubsub_v1/services/publisher/transports/rest.py +++ b/google/pubsub_v1/services/publisher/transports/rest.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/publisher/transports/rest_base.py b/google/pubsub_v1/services/publisher/transports/rest_base.py index dad3a91b2..14308a300 100644 --- a/google/pubsub_v1/services/publisher/transports/rest_base.py +++ b/google/pubsub_v1/services/publisher/transports/rest_base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/schema_service/__init__.py b/google/pubsub_v1/services/schema_service/__init__.py index 570d29e7c..0908014e8 100644 --- a/google/pubsub_v1/services/schema_service/__init__.py +++ b/google/pubsub_v1/services/schema_service/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/schema_service/async_client.py b/google/pubsub_v1/services/schema_service/async_client.py index 88eeb1fba..4ebf3ac94 100644 --- a/google/pubsub_v1/services/schema_service/async_client.py +++ b/google/pubsub_v1/services/schema_service/async_client.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/schema_service/client.py b/google/pubsub_v1/services/schema_service/client.py index 07894782c..40fc381b8 100644 --- a/google/pubsub_v1/services/schema_service/client.py +++ b/google/pubsub_v1/services/schema_service/client.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/schema_service/pagers.py b/google/pubsub_v1/services/schema_service/pagers.py index 8f7080b26..02beaee40 100644 --- a/google/pubsub_v1/services/schema_service/pagers.py +++ b/google/pubsub_v1/services/schema_service/pagers.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/schema_service/transports/__init__.py b/google/pubsub_v1/services/schema_service/transports/__init__.py index 73976e7fb..78c2fa21d 100644 --- a/google/pubsub_v1/services/schema_service/transports/__init__.py +++ b/google/pubsub_v1/services/schema_service/transports/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/schema_service/transports/base.py b/google/pubsub_v1/services/schema_service/transports/base.py index e42f9896f..c1187d1cd 100644 --- a/google/pubsub_v1/services/schema_service/transports/base.py +++ b/google/pubsub_v1/services/schema_service/transports/base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/schema_service/transports/grpc.py b/google/pubsub_v1/services/schema_service/transports/grpc.py index daabd41b9..f089c2724 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py index c321b88c2..f030d0563 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/schema_service/transports/rest.py b/google/pubsub_v1/services/schema_service/transports/rest.py index 1bed03dc0..f4eab1dd8 100644 --- a/google/pubsub_v1/services/schema_service/transports/rest.py +++ b/google/pubsub_v1/services/schema_service/transports/rest.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/schema_service/transports/rest_base.py b/google/pubsub_v1/services/schema_service/transports/rest_base.py index 94312eba7..0ce5285bd 100644 --- a/google/pubsub_v1/services/schema_service/transports/rest_base.py +++ b/google/pubsub_v1/services/schema_service/transports/rest_base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/subscriber/__init__.py b/google/pubsub_v1/services/subscriber/__init__.py index e6994bdf6..0e651adb7 100644 --- a/google/pubsub_v1/services/subscriber/__init__.py +++ b/google/pubsub_v1/services/subscriber/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/subscriber/async_client.py b/google/pubsub_v1/services/subscriber/async_client.py index 9fc50fd31..4ad7306f2 100644 --- a/google/pubsub_v1/services/subscriber/async_client.py +++ b/google/pubsub_v1/services/subscriber/async_client.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index 29c7be980..44f3b1598 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/subscriber/pagers.py b/google/pubsub_v1/services/subscriber/pagers.py index 26adcdd63..9f879cfc8 100644 --- a/google/pubsub_v1/services/subscriber/pagers.py +++ b/google/pubsub_v1/services/subscriber/pagers.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/subscriber/transports/__init__.py b/google/pubsub_v1/services/subscriber/transports/__init__.py index af60aba8e..73e9fd44f 100644 --- a/google/pubsub_v1/services/subscriber/transports/__init__.py +++ b/google/pubsub_v1/services/subscriber/transports/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/subscriber/transports/base.py b/google/pubsub_v1/services/subscriber/transports/base.py index 51b50a55f..01fd7ddcb 100644 --- a/google/pubsub_v1/services/subscriber/transports/base.py +++ b/google/pubsub_v1/services/subscriber/transports/base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/subscriber/transports/grpc.py b/google/pubsub_v1/services/subscriber/transports/grpc.py index ff59214ca..6a10424fe 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py index 08eaf9665..04576b587 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/subscriber/transports/rest.py b/google/pubsub_v1/services/subscriber/transports/rest.py index a44591cf9..62211228e 100644 --- a/google/pubsub_v1/services/subscriber/transports/rest.py +++ b/google/pubsub_v1/services/subscriber/transports/rest.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/services/subscriber/transports/rest_base.py b/google/pubsub_v1/services/subscriber/transports/rest_base.py index 033af0b16..f4fb07656 100644 --- a/google/pubsub_v1/services/subscriber/transports/rest_base.py +++ b/google/pubsub_v1/services/subscriber/transports/rest_base.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/types/__init__.py b/google/pubsub_v1/types/__init__.py index 5f2a260b0..85c6b901b 100644 --- a/google/pubsub_v1/types/__init__.py +++ b/google/pubsub_v1/types/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index 8917db59f..74b65549b 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/types/schema.py b/google/pubsub_v1/types/schema.py index 9353e4817..e1f376ed9 100644 --- a/google/pubsub_v1/types/schema.py +++ b/google/pubsub_v1/types/schema.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_async.py index 5694b24ef..e1bf1f2c1 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_sync.py index 7de319c67..941fea1d4 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_create_topic_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_async.py index 3d2b74803..2fad1b099 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_sync.py index e760eddbd..27b58c27a 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_delete_topic_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_async.py index 86508954b..22fb9e7e6 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_sync.py index af6b45837..058c10e73 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_detach_subscription_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_async.py index a8bea68c2..a8de7a307 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_sync.py index 051b98093..d2846a750 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_get_topic_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_async.py index 4eed18b38..e8a3e2e8d 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_sync.py index 1e2266757..3a51a39b8 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_snapshots_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_async.py index 959f1e824..cbc81e48f 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_sync.py index c648b8d1a..dee0821cd 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topic_subscriptions_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_async.py index dc4a36ef8..0fc18583a 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_sync.py index 8d71be398..2d2a987ee 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_list_topics_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_publish_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_publish_async.py index 5c6b63a2d..536b7f099 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_publish_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_publish_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_publish_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_publish_sync.py index 9af236d44..e89f90320 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_publish_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_publish_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_async.py b/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_async.py index d9451b624..a814eab54 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_async.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_sync.py b/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_sync.py index 9b7326c2c..46c967e4e 100644 --- a/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_publisher_update_topic_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_async.py index b93ec0b5d..e24d459c8 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_sync.py index c7caa1cbb..d3be03abe 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_commit_schema_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_async.py index 9cdd164f5..7eaf44f44 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_sync.py index ce8619977..da7cf76c9 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_create_schema_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_async.py index 1bbebfff5..6fffc7395 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_async.py index a35c3af1b..fa37387cd 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_sync.py index 118fe7340..4d1ac5e19 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_revision_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_sync.py index 77dfe887b..64640ba16 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_delete_schema_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_async.py index 240df5a2a..feb39e86e 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_sync.py index 2d45faebf..cf387dbcf 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_get_schema_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_async.py index 7ea512001..9c2f61ad4 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_sync.py index 6aebac042..08b49520c 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schema_revisions_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_async.py index 963618fa9..7d88f3194 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_sync.py index d7b9e6464..776abc3d4 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_list_schemas_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_async.py index b50a08b18..66628743c 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_sync.py index 4d83c8cac..2a5d2687d 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_rollback_schema_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_async.py index 3dcd25490..127b90fec 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_sync.py index 4f509d6b3..08e3b9142 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_message_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_async.py b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_async.py index 5deb08a44..5cdc6072d 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_async.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_sync.py b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_sync.py index cc7826792..af9792f1e 100644 --- a/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_schema_service_validate_schema_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_async.py index 7fc2967d5..37ea78fa1 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_sync.py index 85b492df0..80cc79a64 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_acknowledge_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_async.py index 3f7c691da..f1084952b 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_sync.py index 7738b03da..207b31599 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_create_snapshot_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_async.py index 306218036..64a7f134d 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_sync.py index cbb05f62a..7efb7a912 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_create_subscription_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_async.py index 5bea031a7..b92fab270 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_sync.py index 01342901b..dd7533eaf 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_snapshot_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_async.py index 08a15f4ed..12c85f95e 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_sync.py index 1cde73a72..c9285d87e 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_delete_subscription_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_async.py index 40960acc7..fd22fe023 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_sync.py index 3ee2e9c62..a027bcddf 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_get_snapshot_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_async.py index 47c739998..12eabdec4 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_sync.py index 22832bc89..13b7ea626 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_get_subscription_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_async.py index bcc3be15d..0d3698773 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_sync.py index 3477e32d3..4568bef48 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_list_snapshots_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_async.py index 7b00831a3..b7811265a 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_sync.py index 8e1a96487..5bdc68dd5 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_list_subscriptions_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_async.py index 8e9372292..4492740cd 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_sync.py index 1d32afec2..d198d4bab 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_ack_deadline_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_async.py index 1d88c9590..155db77c6 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_sync.py index 43ac23e4f..bca872f9d 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_modify_push_config_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_pull_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_pull_async.py index 3e6f26a0b..d351f26cf 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_pull_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_pull_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_pull_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_pull_sync.py index 950f18cc1..e11007592 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_pull_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_pull_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_seek_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_seek_async.py index 9038bfd11..b5eab9a46 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_seek_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_seek_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_seek_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_seek_sync.py index b57af8fb0..8a0063f66 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_seek_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_seek_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_async.py index 332c12895..b2ecd899b 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_sync.py index 47926a80a..2de009269 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_streaming_pull_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_async.py index 833885af5..7aa873ec1 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_sync.py index 1c8f8530d..7cb4af13e 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_update_snapshot_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_async.py b/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_async.py index 5c00a74dc..ed6a5512b 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_async.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_async.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_sync.py b/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_sync.py index 5cc3de6ec..a592001ec 100644 --- a/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_sync.py +++ b/samples/generated_samples/pubsub_v1_generated_subscriber_update_subscription_sync.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/scripts/fixup_pubsub_v1_keywords.py b/scripts/fixup_pubsub_v1_keywords.py index 2e9609f5f..3bb90d780 100644 --- a/scripts/fixup_pubsub_v1_keywords.py +++ b/scripts/fixup_pubsub_v1_keywords.py @@ -1,6 +1,6 @@ #! /usr/bin/env python3 # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/__init__.py b/tests/__init__.py index 8f6cf0682..cbf94b283 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py index 8f6cf0682..cbf94b283 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/unit/gapic/__init__.py b/tests/unit/gapic/__init__.py index 8f6cf0682..cbf94b283 100644 --- a/tests/unit/gapic/__init__.py +++ b/tests/unit/gapic/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/unit/gapic/pubsub_v1/__init__.py b/tests/unit/gapic/pubsub_v1/__init__.py index 8f6cf0682..cbf94b283 100644 --- a/tests/unit/gapic/pubsub_v1/__init__.py +++ b/tests/unit/gapic/pubsub_v1/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/unit/gapic/pubsub_v1/test_publisher.py b/tests/unit/gapic/pubsub_v1/test_publisher.py index 4856b1cad..223679ad1 100644 --- a/tests/unit/gapic/pubsub_v1/test_publisher.py +++ b/tests/unit/gapic/pubsub_v1/test_publisher.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/unit/gapic/pubsub_v1/test_schema_service.py b/tests/unit/gapic/pubsub_v1/test_schema_service.py index 194cddacf..7f8355861 100644 --- a/tests/unit/gapic/pubsub_v1/test_schema_service.py +++ b/tests/unit/gapic/pubsub_v1/test_schema_service.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py index 966e30957..417249e97 100644 --- a/tests/unit/gapic/pubsub_v1/test_subscriber.py +++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2024 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From b9eb331ebccee730e399035228bcb4a3d76fe358 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 11:09:49 -0400 Subject: [PATCH 143/190] chore(main): release 2.29.0 (#1352) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 24 +++++++++++++++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e0b4b7916..15777e3f6 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.28.0" + ".": "2.29.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 63f40ce6a..9ffc0d132 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,30 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.29.0](https://github.com/googleapis/python-pubsub/compare/v2.28.0...v2.29.0) (2025-03-19) + + +### Features + +* Add REST Interceptors which support reading metadata ([4363179](https://github.com/googleapis/python-pubsub/commit/43631790781ccfe071a7ecad41949399d3dbd063)) +* Add support for opt-in debug logging ([4363179](https://github.com/googleapis/python-pubsub/commit/43631790781ccfe071a7ecad41949399d3dbd063)) +* Deprecate `enabled` field for message transforms and add `disabled` field ([4363179](https://github.com/googleapis/python-pubsub/commit/43631790781ccfe071a7ecad41949399d3dbd063)) + + +### Bug Fixes + +* Allow logs to propagate upstream for caplog testing ([#1374](https://github.com/googleapis/python-pubsub/issues/1374)) ([fa39b0e](https://github.com/googleapis/python-pubsub/commit/fa39b0e87695da40036c1daec1b3108374672d61)) +* Allow Protobuf 6.x ([#1369](https://github.com/googleapis/python-pubsub/issues/1369)) ([c95b7a5](https://github.com/googleapis/python-pubsub/commit/c95b7a5bad7138a70e56c278970f5b54939a68f8)) +* Fix typing issue with gRPC metadata when key ends in -bin ([4363179](https://github.com/googleapis/python-pubsub/commit/43631790781ccfe071a7ecad41949399d3dbd063)) + + +### Documentation + +* A comment for field `code` in message `.google.pubsub.v1.JavaScriptUDF` is changed ([4363179](https://github.com/googleapis/python-pubsub/commit/43631790781ccfe071a7ecad41949399d3dbd063)) +* Add samples and test for ingestion from Kafka sources ([#1354](https://github.com/googleapis/python-pubsub/issues/1354)) ([820f986](https://github.com/googleapis/python-pubsub/commit/820f986104ca39fd0c92ba6816319e939be1ed63)) +* Deprecate `enabled` field for message transforms and add `disabled` field ([4363179](https://github.com/googleapis/python-pubsub/commit/43631790781ccfe071a7ecad41949399d3dbd063)) +* **samples:** Increase example max_bytes setting for cloud storage subscriptions to encourage more performant subscribe ([#1324](https://github.com/googleapis/python-pubsub/issues/1324)) ([cb760a7](https://github.com/googleapis/python-pubsub/commit/cb760a71cd4ad035d0c2c4c0f7b66bf52f18808c)) + ## [2.28.0](https://github.com/googleapis/python-pubsub/compare/v2.27.3...v2.28.0) (2025-01-30) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 8f0f03c06..07483fa04 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.28.0" # {x-release-please-version} +__version__ = "2.29.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 8f0f03c06..07483fa04 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.28.0" # {x-release-please-version} +__version__ = "2.29.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index dd0d6423a..9f802d89d 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "0.1.0" + "version": "2.29.0" }, "snippets": [ { From be9005412fea06bea917c8b6861546b7e6c62a1e Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 17:46:37 -0400 Subject: [PATCH 144/190] docs: update documentation for JavaScriptUDF to indicate that the `message_id` metadata field is optional instead of required (#1380) Co-authored-by: Owl Bot --- google/pubsub_v1/types/pubsub.py | 2 +- .../generated_samples/snippet_metadata_google.pubsub.v1.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index 74b65549b..6f27edfee 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -1119,7 +1119,7 @@ class JavaScriptUDF(proto.Message): * * @param {Object} metadata - Pub/Sub message metadata. * Keys: - * - (required) 'message_id' : {string} + * - (optional) 'message_id' : {string} * - (optional) 'publish_time': {string} YYYY-MM-DDTHH:MM:SSZ format * - (optional) 'ordering_key': {string} */ diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 9f802d89d..dd0d6423a 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.29.0" + "version": "0.1.0" }, "snippets": [ { From da0e6d0dd871244f1572c7307e6c9729dfcb62bb Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 08:29:17 -0700 Subject: [PATCH 145/190] chore(python): remove noxfile.py from templates (#1383) Co-authored-by: Owl Bot Co-authored-by: Anthonios Partheniou --- .github/.OwlBot.lock.yaml | 4 +- owlbot.py | 92 +-------------------------------------- 2 files changed, 3 insertions(+), 93 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index c631e1f7d..508ba98ef 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:5581906b957284864632cde4e9c51d1cc66b0094990b27e689132fe5cd036046 -# created: 2025-03-05 + digest: sha256:25de45b58e52021d3a24a6273964371a97a4efeefe6ad3845a64e697c63b6447 +# created: 2025-04-14T14:34:43.260858345Z diff --git a/owlbot.py b/owlbot.py index a8044bd6f..3aa352620 100644 --- a/owlbot.py +++ b/owlbot.py @@ -326,7 +326,7 @@ if count < 1: raise Exception(".coveragerc replacement failed.") - s.move([library], excludes=["**/gapic_version.py", "README.rst", "docs/**/*", "setup.py", "testing/constraints-3.7.txt", "testing/constraints-3.8.txt"]) + s.move([library], excludes=["**/gapic_version.py", "noxfile.py", "README.rst", "docs/**/*", "setup.py", "testing/constraints-3.7.txt", "testing/constraints-3.8.txt"]) s.remove_staging_dirs() # ---------------------------------------------------------------------------- @@ -345,96 +345,6 @@ ) s.move(templated_files, excludes=[".coveragerc", ".github/release-please.yml", "README.rst", "docs/index.rst"]) -# ---------------------------------------------------------------------------- -# Add mypy nox session. -# ---------------------------------------------------------------------------- -s.replace( - "noxfile.py", - r"LINT_PATHS = \[.*?\]", - '\g<0>\n\nMYPY_VERSION = "mypy==1.10.0"', -) -s.replace( - "noxfile.py", r'"blacken",', '\g<0>\n "mypy",', -) -s.replace( - "noxfile.py", - r"nox\.options\.error_on_missing_interpreters = True", - textwrap.dedent( - ''' \g<0> - - - @nox.session(python=DEFAULT_PYTHON_VERSION) - def mypy(session): - """Run type checks with mypy.""" - session.install("-e", ".[all]") - session.install(MYPY_VERSION) - - # Version 2.1.1 of google-api-core version is the first type-checked release. - # Version 2.2.0 of google-cloud-core version is the first type-checked release. - session.install( - "google-api-core[grpc]>=2.1.1", - "google-cloud-core>=2.2.0", - ) - - # Just install the type info directly, since "mypy --install-types" might - # require an additional pass. - # Exclude types-protobuf==4.24.0.20240106 - # See https://github.com/python/typeshed/issues/11254 - session.install("types-protobuf!=4.24.0.20240106", "types-setuptools") - - # TODO: Only check the hand-written layer, the generated code does not pass - # mypy checks yet. - # https://github.com/googleapis/gapic-generator-python/issues/1092 - session.run("mypy", "-p", "google.cloud")''' - ), -) - - -# ---------------------------------------------------------------------------- -# Add mypy_samples nox session. -# ---------------------------------------------------------------------------- -s.replace( - "noxfile.py", - r' "mypy",', - '\g<0>\n # https://github.com/googleapis/python-pubsub/pull/552#issuecomment-1016256936' - '\n # "mypy_samples", # TODO: uncomment when the check passes', -) -s.replace( - "noxfile.py", - r'session\.run\("mypy", "-p", "google.cloud"\)', - textwrap.dedent( - ''' \g<0> - - - @nox.session(python=DEFAULT_PYTHON_VERSION) - def mypy_samples(session): - """Run type checks with mypy.""" - - session.install("-e", ".[all]") - - session.install("pytest") - session.install(MYPY_VERSION) - - # Just install the type info directly, since "mypy --install-types" might - # require an additional pass. - session.install("types-mock", "types-protobuf", "types-setuptools") - - session.run( - "mypy", - "--config-file", - str(CURRENT_DIRECTORY / "samples" / "snippets" / "mypy.ini"), - "--no-incremental", # Required by warn-unused-configs from mypy.ini to work - "samples/", - )''' - ), -) - - -# Only consider the hand-written layer when assessing the test coverage. -s.replace( - "noxfile.py", "--cov=google", "--cov=google/cloud", -) - s.replace(".github/blunderbuss.yml", "googleapis/api-pubsub", "mukund-ananthu") python.py_samples(skip_readmes=True) From 4d072e088b59f692dc3d59c3197a2993c125917e Mon Sep 17 00:00:00 2001 From: Kamal Aboul-Hosn Date: Mon, 28 Apr 2025 17:01:17 -0400 Subject: [PATCH 146/190] docs(sample): Add samples for topic and subscription SMTs (#1386) Co-authored-by: mukund-ananthu <83691193+mukund-ananthu@users.noreply.github.com> --- samples/snippets/publisher.py | 43 +++++++++++++++++++++++++ samples/snippets/publisher_test.py | 20 ++++++++++++ samples/snippets/subscriber.py | 49 +++++++++++++++++++++++++++++ samples/snippets/subscriber_test.py | 31 ++++++++++++++++++ 4 files changed, 143 insertions(+) diff --git a/samples/snippets/publisher.py b/samples/snippets/publisher.py index e279324b8..d2b6dd2b8 100644 --- a/samples/snippets/publisher.py +++ b/samples/snippets/publisher.py @@ -326,6 +326,39 @@ def create_topic_with_confluent_cloud_ingestion( # [END pubsub_create_topic_with_confluent_cloud_ingestion] +def create_topic_with_smt( + project_id: str, + topic_id: str, +) -> None: + """Create a new Pub/Sub topic with a UDF SMT.""" + # [START pubsub_create_topic_with_smt] + from google.cloud import pubsub_v1 + from google.pubsub_v1.types import JavaScriptUDF, MessageTransform, Topic + + # TODO(developer) + # project_id = "your-project-id" + # topic_id = "your-topic-id" + + code = """function redactSSN(message, metadata) { + const data = JSON.parse(message.data); + delete data['ssn']; + message.data = JSON.stringify(data); + return message; + }""" + udf = JavaScriptUDF(code=code, function_name="redactSSN") + transforms = [MessageTransform(javascript_udf=udf)] + + publisher = pubsub_v1.PublisherClient() + topic_path = publisher.topic_path(project_id, topic_id) + + request = Topic(name=topic_path, message_transforms=transforms) + + topic = publisher.create_topic(request=request) + + print(f"Created topic: {topic.name} with SMT") + # [END pubsub_create_topic_with_smt] + + def update_topic_type( project_id: str, topic_id: str, @@ -888,6 +921,11 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: "gcp_service_account" ) + create_parser = subparsers.add_parser( + "create_smt", help=create_topic_with_smt.__doc__ + ) + create_parser.add_argument("topic_id") + update_topic_type_parser = subparsers.add_parser( "update_kinesis_ingestion", help=update_topic_type.__doc__ ) @@ -1007,6 +1045,11 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: args.identity_pool_id, args.gcp_service_account, ) + elif args.command == "create_smt": + create_topic_with_smt( + args.project_id, + args.topic_id, + ) elif args.command == "update_kinesis_ingestion": update_topic_type( args.project_id, diff --git a/samples/snippets/publisher_test.py b/samples/snippets/publisher_test.py index dc7b94027..1c691bd5c 100644 --- a/samples/snippets/publisher_test.py +++ b/samples/snippets/publisher_test.py @@ -313,6 +313,26 @@ def test_create_topic_with_confluent_cloud_ingestion( publisher_client.delete_topic(request={"topic": topic_path}) +def test_create_with_smt( + publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] +) -> None: + # The scope of `topic_path` is limited to this function. + topic_path = publisher_client.topic_path(PROJECT_ID, TOPIC_ID) + + try: + publisher_client.delete_topic(request={"topic": topic_path}) + except NotFound: + pass + + publisher.create_topic_with_smt(PROJECT_ID, TOPIC_ID) + + out, _ = capsys.readouterr() + assert f"Created topic: {topic_path} with SMT" in out + + # Clean up resource created for the test. + publisher_client.delete_topic(request={"topic": topic_path}) + + def test_update_topic_type( publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] ) -> None: diff --git a/samples/snippets/subscriber.py b/samples/snippets/subscriber.py index c09f5def1..5549d056f 100644 --- a/samples/snippets/subscriber.py +++ b/samples/snippets/subscriber.py @@ -578,6 +578,45 @@ def create_cloudstorage_subscription( # [END pubsub_create_cloud_storage_subscription] +def create_subscription_with_smt( + project_id: str, topic_id: str, subscription_id: str +) -> None: + """Create a subscription with a UDF SMT.""" + # [START pubsub_create_subscription_with_smt] + from google.cloud import pubsub_v1 + from google.pubsub_v1.types import JavaScriptUDF, MessageTransform + + # TODO(developer): Choose an existing topic. + # project_id = "your-project-id" + # topic_id = "your-topic-id" + # subscription_id = "your-subscription-id" + + publisher = pubsub_v1.PublisherClient() + subscriber = pubsub_v1.SubscriberClient() + topic_path = publisher.topic_path(project_id, topic_id) + subscription_path = subscriber.subscription_path(project_id, subscription_id) + + code = """function redactSSN(message, metadata) { + const data = JSON.parse(message.data); + delete data['ssn']; + message.data = JSON.stringify(data); + return message; + }""" + udf = JavaScriptUDF(code=code, function_name="redactSSN") + transforms = [MessageTransform(javascript_udf=udf)] + + with subscriber: + subscription = subscriber.create_subscription( + request={ + "name": subscription_path, + "topic": topic_path, + "message_transforms": transforms, + } + ) + print(f"Created subscription with SMT: {subscription}") + # [END pubsub_create_subscription_with_smt] + + def delete_subscription(project_id: str, subscription_id: str) -> None: """Deletes an existing Pub/Sub topic.""" # [START pubsub_delete_subscription] @@ -1310,6 +1349,12 @@ def callback(message: pubsub_v1.subscriber.message.Message) -> None: create_cloudstorage_subscription_parser.add_argument("subscription_id") create_cloudstorage_subscription_parser.add_argument("bucket") + create_subscription_with_smt_parser = subparsers.add_parser( + "create-with-smt", help=create_subscription_with_smt.__doc__ + ) + create_subscription_with_smt_parser.add_argument("topic_id") + create_subscription_with_smt_parser.add_argument("subscription_id") + delete_parser = subparsers.add_parser("delete", help=delete_subscription.__doc__) delete_parser.add_argument("subscription_id") @@ -1471,6 +1516,10 @@ def callback(message: pubsub_v1.subscriber.message.Message) -> None: create_cloudstorage_subscription( args.project_id, args.topic_id, args.subscription_id, args.bucket ) + elif args.command == "create-with-smt": + create_subscription_with_smt( + args.project_id, args.topic_id, args.subscription_id + ) elif args.command == "delete": delete_subscription(args.project_id, args.subscription_id) diff --git a/samples/snippets/subscriber_test.py b/samples/snippets/subscriber_test.py index 86f7a94ce..53a844e01 100644 --- a/samples/snippets/subscriber_test.py +++ b/samples/snippets/subscriber_test.py @@ -579,6 +579,37 @@ def test_create_push_subscription( subscriber_client.delete_subscription(request={"subscription": subscription_path}) +def test_create_subscription_with_smt( + subscriber_client: pubsub_v1.SubscriberClient, + topic: str, + capsys: CaptureFixture[str], +) -> None: + subscription_for_create_name = ( + f"subscription-test-subscription-for-create-with-smt-{PY_VERSION}-{UUID}" + ) + + subscription_path = subscriber_client.subscription_path( + PROJECT_ID, subscription_for_create_name + ) + + try: + subscriber_client.delete_subscription( + request={"subscription": subscription_path} + ) + except NotFound: + pass + + subscriber.create_subscription_with_smt( + PROJECT_ID, TOPIC, subscription_for_create_name + ) + + out, _ = capsys.readouterr() + assert f"{subscription_for_create_name}" in out + + # Clean up. + subscriber_client.delete_subscription(request={"subscription": subscription_path}) + + def test_update_push_subscription( subscriber_client: pubsub_v1.SubscriberClient, topic: str, From 77ba05d4ba5b84a25c1a07c5397bbc184fa6041d Mon Sep 17 00:00:00 2001 From: Dan Lee <71398022+dandhlee@users.noreply.github.com> Date: Wed, 21 May 2025 06:05:34 -0400 Subject: [PATCH 147/190] docs: update readme links (#1409) --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index dd3032e00..97010e998 100644 --- a/README.rst +++ b/README.rst @@ -27,7 +27,7 @@ independently written applications. :target: https://pypi.org/project/google-cloud-pubsub/ .. _Google Cloud Pub / Sub: https://cloud.google.com/pubsub/ .. _Product Documentation: https://cloud.google.com/pubsub/docs -.. _Client Library Documentation: https://cloud.google.com/python/docs/reference/pubsub/latest +.. _Client Library Documentation: https://cloud.google.com/python/docs/reference/pubsub/latest/summary_overview Quick Start ----------- @@ -116,7 +116,7 @@ messages to it To learn more, consult the `publishing documentation`_. -.. _publishing documentation: https://cloud.google.com/python/docs/reference/pubsub/latest +.. _publishing documentation: https://cloud.google.com/python/docs/reference/pubsub/latest/google.cloud.pubsub_v1.publisher.client.Client Subscribing @@ -162,7 +162,7 @@ block the current thread until a given condition obtains: It is also possible to pull messages in a synchronous (blocking) fashion. To learn more about subscribing, consult the `subscriber documentation`_. -.. _subscriber documentation: https://cloud.google.com/python/docs/reference/pubsub/latest +.. _subscriber documentation: https://cloud.google.com/python/docs/reference/pubsub/latest/google.cloud.pubsub_v1.subscriber.client.Client Authentication From a51d8f64b94c327f2bd47343038dc51ee5a1d8b3 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 06:07:07 -0400 Subject: [PATCH 148/190] chore: Update gapic-generator-python to 1.25.0 (#1385) Co-authored-by: Owl Bot --- .flake8 | 15 ++++++++------- MANIFEST.in | 13 ++++--------- .../services/publisher/async_client.py | 4 ++++ google/pubsub_v1/services/publisher/client.py | 3 +++ .../services/publisher/transports/base.py | 4 ++++ .../services/publisher/transports/grpc.py | 3 +-- .../services/publisher/transports/rest.py | 4 ++++ .../services/schema_service/async_client.py | 4 ++++ .../pubsub_v1/services/schema_service/client.py | 3 +++ .../services/schema_service/transports/base.py | 4 ++++ .../services/schema_service/transports/grpc.py | 3 +-- .../services/schema_service/transports/rest.py | 4 ++++ .../services/subscriber/async_client.py | 4 ++++ google/pubsub_v1/services/subscriber/client.py | 3 +++ .../services/subscriber/transports/base.py | 4 ++++ .../services/subscriber/transports/grpc.py | 3 +-- .../services/subscriber/transports/rest.py | 4 ++++ testing/constraints-3.13.txt | 17 +++++++++++------ 18 files changed, 71 insertions(+), 28 deletions(-) diff --git a/.flake8 b/.flake8 index 32986c792..90316de21 100644 --- a/.flake8 +++ b/.flake8 @@ -1,28 +1,29 @@ # -*- coding: utf-8 -*- -# -# Copyright 2024 Google LLC +# Copyright 2025 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 +# 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. - -# Generated by synthtool. DO NOT EDIT! +# [flake8] +# TODO(https://github.com/googleapis/gapic-generator-python/issues/2333): +# Resolve flake8 lint issues ignore = E203, E231, E266, E501, W503 exclude = - # Exclude generated code. - **/proto/** + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2333): + # Ensure that generated code passes flake8 lint **/gapic/** **/services/** **/types/** + # Exclude Protobuf gencode *_pb2.py # Standard linting exemptions. diff --git a/MANIFEST.in b/MANIFEST.in index d6814cd60..dae249ec8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,25 +1,20 @@ # -*- coding: utf-8 -*- -# -# Copyright 2024 Google LLC +# Copyright 2025 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 +# 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. - -# Generated by synthtool. DO NOT EDIT! +# include README.rst LICENSE -recursive-include google *.json *.proto py.typed +recursive-include google *.py *.pyi *.json *.proto py.typed recursive-include tests * global-exclude *.py[co] global-exclude __pycache__ - -# Exclude scripts for samples readmegen -prune scripts/readme-gen diff --git a/google/pubsub_v1/services/publisher/async_client.py b/google/pubsub_v1/services/publisher/async_client.py index 87838c006..52f1f1e30 100644 --- a/google/pubsub_v1/services/publisher/async_client.py +++ b/google/pubsub_v1/services/publisher/async_client.py @@ -37,6 +37,7 @@ from google.api_core import retry_async as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf try: @@ -1662,5 +1663,8 @@ async def __aexit__(self, exc_type, exc, tb): client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + __all__ = ("PublisherAsyncClient",) diff --git a/google/pubsub_v1/services/publisher/client.py b/google/pubsub_v1/services/publisher/client.py index c94cec688..c8f39273e 100644 --- a/google/pubsub_v1/services/publisher/client.py +++ b/google/pubsub_v1/services/publisher/client.py @@ -47,6 +47,7 @@ from google.auth.transport.grpc import SslCredentials # type: ignore from google.auth.exceptions import MutualTLSChannelError # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf try: OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] @@ -2129,5 +2130,7 @@ def test_iam_permissions( client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ __all__ = ("PublisherClient",) diff --git a/google/pubsub_v1/services/publisher/transports/base.py b/google/pubsub_v1/services/publisher/transports/base.py index 354a6c714..0fb41c922 100644 --- a/google/pubsub_v1/services/publisher/transports/base.py +++ b/google/pubsub_v1/services/publisher/transports/base.py @@ -25,6 +25,7 @@ from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -35,6 +36,9 @@ client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + class PublisherTransport(abc.ABC): """Abstract transport class for Publisher.""" diff --git a/google/pubsub_v1/services/publisher/transports/grpc.py b/google/pubsub_v1/services/publisher/transports/grpc.py index f27b968a8..9e8ed1737 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc.py +++ b/google/pubsub_v1/services/publisher/transports/grpc.py @@ -73,12 +73,11 @@ def intercept_unary_unary(self, continuation, client_call_details, request): f"Sending request for {client_call_details.method}", extra={ "serviceName": "google.pubsub.v1.Publisher", - "rpcName": client_call_details.method, + "rpcName": str(client_call_details.method), "request": grpc_request, "metadata": grpc_request["metadata"], }, ) - response = continuation(client_call_details, request) if logging_enabled: # pragma: NO COVER response_metadata = response.trailing_metadata() diff --git a/google/pubsub_v1/services/publisher/transports/rest.py b/google/pubsub_v1/services/publisher/transports/rest.py index a5c99da1b..92ee46e15 100644 --- a/google/pubsub_v1/services/publisher/transports/rest.py +++ b/google/pubsub_v1/services/publisher/transports/rest.py @@ -23,6 +23,7 @@ from google.api_core import rest_helpers from google.api_core import rest_streaming from google.api_core import gapic_v1 +import google.protobuf from google.protobuf import json_format from google.iam.v1 import iam_policy_pb2 # type: ignore @@ -61,6 +62,9 @@ rest_version=f"requests@{requests_version}", ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + class PublisherRestInterceptor: """Interceptor for Publisher. diff --git a/google/pubsub_v1/services/schema_service/async_client.py b/google/pubsub_v1/services/schema_service/async_client.py index 4ebf3ac94..b2d139fa0 100644 --- a/google/pubsub_v1/services/schema_service/async_client.py +++ b/google/pubsub_v1/services/schema_service/async_client.py @@ -37,6 +37,7 @@ from google.api_core import retry_async as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf try: @@ -1775,5 +1776,8 @@ async def __aexit__(self, exc_type, exc, tb): client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + __all__ = ("SchemaServiceAsyncClient",) diff --git a/google/pubsub_v1/services/schema_service/client.py b/google/pubsub_v1/services/schema_service/client.py index 40fc381b8..493ffd2b6 100644 --- a/google/pubsub_v1/services/schema_service/client.py +++ b/google/pubsub_v1/services/schema_service/client.py @@ -46,6 +46,7 @@ from google.auth.transport.grpc import SslCredentials # type: ignore from google.auth.exceptions import MutualTLSChannelError # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf try: OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] @@ -2190,5 +2191,7 @@ def test_iam_permissions( client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ __all__ = ("SchemaServiceClient",) diff --git a/google/pubsub_v1/services/schema_service/transports/base.py b/google/pubsub_v1/services/schema_service/transports/base.py index c1187d1cd..c30773b74 100644 --- a/google/pubsub_v1/services/schema_service/transports/base.py +++ b/google/pubsub_v1/services/schema_service/transports/base.py @@ -25,6 +25,7 @@ from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -36,6 +37,9 @@ client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + class SchemaServiceTransport(abc.ABC): """Abstract transport class for SchemaService.""" diff --git a/google/pubsub_v1/services/schema_service/transports/grpc.py b/google/pubsub_v1/services/schema_service/transports/grpc.py index f089c2724..1a746ef43 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc.py @@ -74,12 +74,11 @@ def intercept_unary_unary(self, continuation, client_call_details, request): f"Sending request for {client_call_details.method}", extra={ "serviceName": "google.pubsub.v1.SchemaService", - "rpcName": client_call_details.method, + "rpcName": str(client_call_details.method), "request": grpc_request, "metadata": grpc_request["metadata"], }, ) - response = continuation(client_call_details, request) if logging_enabled: # pragma: NO COVER response_metadata = response.trailing_metadata() diff --git a/google/pubsub_v1/services/schema_service/transports/rest.py b/google/pubsub_v1/services/schema_service/transports/rest.py index f4eab1dd8..6d8902c5d 100644 --- a/google/pubsub_v1/services/schema_service/transports/rest.py +++ b/google/pubsub_v1/services/schema_service/transports/rest.py @@ -23,6 +23,7 @@ from google.api_core import rest_helpers from google.api_core import rest_streaming from google.api_core import gapic_v1 +import google.protobuf from google.protobuf import json_format from google.iam.v1 import iam_policy_pb2 # type: ignore @@ -62,6 +63,9 @@ rest_version=f"requests@{requests_version}", ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + class SchemaServiceRestInterceptor: """Interceptor for SchemaService. diff --git a/google/pubsub_v1/services/subscriber/async_client.py b/google/pubsub_v1/services/subscriber/async_client.py index 4ad7306f2..5d3dd3da7 100644 --- a/google/pubsub_v1/services/subscriber/async_client.py +++ b/google/pubsub_v1/services/subscriber/async_client.py @@ -41,6 +41,7 @@ from google.api_core import retry_async as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf try: @@ -2635,5 +2636,8 @@ async def __aexit__(self, exc_type, exc, tb): client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + __all__ = ("SubscriberAsyncClient",) diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index 44f3b1598..eef3ab77d 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -49,6 +49,7 @@ from google.auth.transport.grpc import SslCredentials # type: ignore from google.auth.exceptions import MutualTLSChannelError # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf try: OptionalRetry = Union[retries.Retry, gapic_v1.method._MethodDefault, None] @@ -3087,5 +3088,7 @@ def test_iam_permissions( client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ __all__ = ("SubscriberClient",) diff --git a/google/pubsub_v1/services/subscriber/transports/base.py b/google/pubsub_v1/services/subscriber/transports/base.py index 01fd7ddcb..cc62113f9 100644 --- a/google/pubsub_v1/services/subscriber/transports/base.py +++ b/google/pubsub_v1/services/subscriber/transports/base.py @@ -25,6 +25,7 @@ from google.api_core import retry as retries from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore +import google.protobuf from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore @@ -35,6 +36,9 @@ client_library_version=package_version.__version__ ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + class SubscriberTransport(abc.ABC): """Abstract transport class for Subscriber.""" diff --git a/google/pubsub_v1/services/subscriber/transports/grpc.py b/google/pubsub_v1/services/subscriber/transports/grpc.py index 6a10424fe..ec400da0c 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc.py @@ -73,12 +73,11 @@ def intercept_unary_unary(self, continuation, client_call_details, request): f"Sending request for {client_call_details.method}", extra={ "serviceName": "google.pubsub.v1.Subscriber", - "rpcName": client_call_details.method, + "rpcName": str(client_call_details.method), "request": grpc_request, "metadata": grpc_request["metadata"], }, ) - response = continuation(client_call_details, request) if logging_enabled: # pragma: NO COVER response_metadata = response.trailing_metadata() diff --git a/google/pubsub_v1/services/subscriber/transports/rest.py b/google/pubsub_v1/services/subscriber/transports/rest.py index 62211228e..bb0ee68c6 100644 --- a/google/pubsub_v1/services/subscriber/transports/rest.py +++ b/google/pubsub_v1/services/subscriber/transports/rest.py @@ -23,6 +23,7 @@ from google.api_core import rest_helpers from google.api_core import rest_streaming from google.api_core import gapic_v1 +import google.protobuf from google.protobuf import json_format from google.iam.v1 import iam_policy_pb2 # type: ignore @@ -61,6 +62,9 @@ rest_version=f"requests@{requests_version}", ) +if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"): # pragma: NO COVER + DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__ + class SubscriberRestInterceptor: """Interceptor for Subscriber. diff --git a/testing/constraints-3.13.txt b/testing/constraints-3.13.txt index ad3f0fa58..2010e549c 100644 --- a/testing/constraints-3.13.txt +++ b/testing/constraints-3.13.txt @@ -1,7 +1,12 @@ -# -*- coding: utf-8 -*- -# This constraints file is required for unit tests. +# We use the constraints file for the latest Python version +# (currently this file) to check that the latest +# major versions of dependencies are supported in setup.py. # List all library dependencies and extras in this file. -google-api-core -proto-plus -protobuf -grpc-google-iam-v1 +# Require the latest major version be installed for each dependency. +# e.g., if setup.py has "google-cloud-foo >= 1.14.0, < 2.0.0", +# Then this file should have google-cloud-foo>=1 +google-api-core>=2 +google-auth>=2 +proto-plus>=1 +protobuf>=6 +grpc-google-iam-v1>=0 From 60639c4928105ae8a72c8e37b1f48f75cc2ffcc3 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Wed, 21 May 2025 06:09:16 -0400 Subject: [PATCH 149/190] fix: remove setup.cfg configuration for creating universal wheels (#1376) Co-authored-by: ohmayr --- setup.cfg | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 052350089..000000000 --- a/setup.cfg +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -# -# 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 -# -# 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. - -# Generated by synthtool. DO NOT EDIT! -[bdist_wheel] -universal = 1 From c7ee6134e49122beb21158b2f3f261d7bc00887d Mon Sep 17 00:00:00 2001 From: Andrew Browne <81702808+abbrowne126@users.noreply.github.com> Date: Wed, 21 May 2025 16:53:20 -0400 Subject: [PATCH 150/190] chore: change assignees for issues and PRs to abbrowne126 (#1410) --- .github/blunderbuss.yml | 6 +++--- owlbot.py | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/blunderbuss.yml b/.github/blunderbuss.yml index 3408b580a..ac5c87339 100644 --- a/.github/blunderbuss.yml +++ b/.github/blunderbuss.yml @@ -4,14 +4,14 @@ # Note: This file is autogenerated. To make changes to the assignee # team, please update `codeowner_team` in `.repo-metadata.json`. assign_issues: - - mukund-ananthu + - abbrowne126 assign_issues_by: - labels: - "samples" to: - googleapis/python-samples-reviewers - - mukund-ananthu + - abbrowne126 assign_prs: - - mukund-ananthu + - abbrowne126 diff --git a/owlbot.py b/owlbot.py index 3aa352620..d845e5758 100644 --- a/owlbot.py +++ b/owlbot.py @@ -343,9 +343,7 @@ system_test_python_versions=["3.12"], system_test_external_dependencies=["psutil","flaky"], ) -s.move(templated_files, excludes=[".coveragerc", ".github/release-please.yml", "README.rst", "docs/index.rst"]) - -s.replace(".github/blunderbuss.yml", "googleapis/api-pubsub", "mukund-ananthu") +s.move(templated_files, excludes=[".coveragerc", ".github/blunderbuss.yml", ".github/release-please.yml", "README.rst", "docs/index.rst"]) python.py_samples(skip_readmes=True) From 2a00b13269b352795cd3a14049202a82fe9fe43a Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Fri, 23 May 2025 21:51:22 +0200 Subject: [PATCH 151/190] chore(deps): update all dependencies (#1412) Co-authored-by: Anthonios Partheniou --- samples/snippets/requirements-test.txt | 9 +++++---- samples/snippets/requirements.txt | 11 ++++++----- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index aa57a68a2..2bf14b760 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1,7 +1,8 @@ backoff==2.2.1 pytest===7.4.4; python_version == '3.7' -pytest==8.3.4; python_version >= '3.8' -mock==5.1.0 +pytest==8.3.5; python_version >= '3.8' +mock==5.2.0 flaky==3.8.1 -google-cloud-bigquery==3.27.0 -google-cloud-storage==2.19.0 +google-cloud-bigquery==3.30.0; python_version < '3.9' +google-cloud-bigquery==3.33.0; python_version >= '3.9' +google-cloud-storage==3.1.0 diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index b6ae767c6..83202d4f8 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,10 +1,11 @@ -google-cloud-pubsub==2.28.0 +google-cloud-pubsub==2.29.0 avro==1.12.0 protobuf===4.24.4; python_version == '3.7' -protobuf==5.29.2; python_version >= '3.8' +protobuf===5.29.4; python_version == '3.8' +protobuf==6.31.0; python_version >= '3.9' avro==1.12.0 opentelemetry-api===1.22.0; python_version == '3.7' opentelemetry-sdk===1.22.0; python_version == '3.7' -opentelemetry-api==1.29.0; python_version >= '3.8' -opentelemetry-sdk==1.29.0; python_version >= '3.8' -opentelemetry-exporter-gcp-trace==1.7.0 +opentelemetry-api==1.33.1; python_version >= '3.8' +opentelemetry-sdk==1.33.1; python_version >= '3.8' +opentelemetry-exporter-gcp-trace==1.9.0 From 42d32de6d6f9e765a97c1ff8a1f9bffbbfc741b0 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 13:15:28 -0400 Subject: [PATCH 152/190] chore(main): release 2.29.1 (#1381) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 15777e3f6..db87e5c90 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.29.0" + ".": "2.29.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ffc0d132..1435f6752 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.29.1](https://github.com/googleapis/python-pubsub/compare/v2.29.0...v2.29.1) (2025-05-23) + + +### Bug Fixes + +* Remove setup.cfg configuration for creating universal wheels ([#1376](https://github.com/googleapis/python-pubsub/issues/1376)) ([60639c4](https://github.com/googleapis/python-pubsub/commit/60639c4928105ae8a72c8e37b1f48f75cc2ffcc3)) + + +### Documentation + +* **sample:** Add samples for topic and subscription SMTs ([#1386](https://github.com/googleapis/python-pubsub/issues/1386)) ([4d072e0](https://github.com/googleapis/python-pubsub/commit/4d072e088b59f692dc3d59c3197a2993c125917e)) +* Update documentation for JavaScriptUDF to indicate that the `message_id` metadata field is optional instead of required ([#1380](https://github.com/googleapis/python-pubsub/issues/1380)) ([be90054](https://github.com/googleapis/python-pubsub/commit/be9005412fea06bea917c8b6861546b7e6c62a1e)) +* Update readme links ([#1409](https://github.com/googleapis/python-pubsub/issues/1409)) ([77ba05d](https://github.com/googleapis/python-pubsub/commit/77ba05d4ba5b84a25c1a07c5397bbc184fa6041d)) + ## [2.29.0](https://github.com/googleapis/python-pubsub/compare/v2.28.0...v2.29.0) (2025-03-19) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 07483fa04..f62042190 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.29.0" # {x-release-please-version} +__version__ = "2.29.1" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 07483fa04..f62042190 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.29.0" # {x-release-please-version} +__version__ = "2.29.1" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index dd0d6423a..dbb835bb3 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "0.1.0" + "version": "2.29.1" }, "snippets": [ { From c046ca22e9bddff6b50f7670bf6b9b9470bf78e8 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 17:03:00 -0700 Subject: [PATCH 153/190] feat: Add SchemaViolationReason to IngestionFailureEvent (#1411) Co-authored-by: Owl Bot Co-authored-by: Andrew Browne <81702808+abbrowne126@users.noreply.github.com> --- .../services/subscriber/async_client.py | 4 +- .../pubsub_v1/services/subscriber/client.py | 4 +- .../services/subscriber/transports/grpc.py | 2 +- .../subscriber/transports/grpc_asyncio.py | 2 +- google/pubsub_v1/types/pubsub.py | 169 +++++++++++++++--- 5 files changed, 154 insertions(+), 27 deletions(-) diff --git a/google/pubsub_v1/services/subscriber/async_client.py b/google/pubsub_v1/services/subscriber/async_client.py index 5d3dd3da7..77f96d6df 100644 --- a/google/pubsub_v1/services/subscriber/async_client.py +++ b/google/pubsub_v1/services/subscriber/async_client.py @@ -1377,7 +1377,7 @@ def streaming_pull( metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> Awaitable[AsyncIterable[pubsub.StreamingPullResponse]]: r"""Establishes a stream with the server, which sends messages down - to the client. The client streams acknowledgements and ack + to the client. The client streams acknowledgments and ack deadline modifications back to the server. The server will close the stream and return the status on any error. The server may close the stream with status ``UNAVAILABLE`` to reassign @@ -1427,7 +1427,7 @@ def request_generator(): requests (AsyncIterator[`google.pubsub_v1.types.StreamingPullRequest`]): The request object AsyncIterator. Request for the ``StreamingPull`` streaming RPC method. This request is used to establish the initial stream as - well as to stream acknowledgements and ack deadline + well as to stream acknowledgments and ack deadline modifications from the client to the server. retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, should be retried. diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index eef3ab77d..ff7edf538 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -1827,7 +1827,7 @@ def streaming_pull( metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), ) -> Iterable[pubsub.StreamingPullResponse]: r"""Establishes a stream with the server, which sends messages down - to the client. The client streams acknowledgements and ack + to the client. The client streams acknowledgments and ack deadline modifications back to the server. The server will close the stream and return the status on any error. The server may close the stream with status ``UNAVAILABLE`` to reassign @@ -1877,7 +1877,7 @@ def request_generator(): requests (Iterator[google.pubsub_v1.types.StreamingPullRequest]): The request object iterator. Request for the ``StreamingPull`` streaming RPC method. This request is used to establish the initial stream as - well as to stream acknowledgements and ack deadline + well as to stream acknowledgments and ack deadline modifications from the client to the server. retry (google.api_core.retry.Retry): Designation of what errors, if any, should be retried. diff --git a/google/pubsub_v1/services/subscriber/transports/grpc.py b/google/pubsub_v1/services/subscriber/transports/grpc.py index ec400da0c..36c77648b 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc.py @@ -571,7 +571,7 @@ def streaming_pull( r"""Return a callable for the streaming pull method over gRPC. Establishes a stream with the server, which sends messages down - to the client. The client streams acknowledgements and ack + to the client. The client streams acknowledgments and ack deadline modifications back to the server. The server will close the stream and return the status on any error. The server may close the stream with status ``UNAVAILABLE`` to reassign diff --git a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py index 04576b587..78f8afd4c 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py @@ -585,7 +585,7 @@ def streaming_pull( r"""Return a callable for the streaming pull method over gRPC. Establishes a stream with the server, which sends messages down - to the client. The client streams acknowledgements and ack + to the client. The client streams acknowledgments and ack deadline modifications back to the server. The server will close the stream and return the status on any error. The server may close the stream with status ``UNAVAILABLE`` to reassign diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index 6f27edfee..dcc25bb31 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -827,6 +827,11 @@ class IngestionFailureEvent(proto.Message): Optional. Failure when ingesting from Confluent Cloud. + This field is a member of `oneof`_ ``failure``. + aws_kinesis_failure (google.pubsub_v1.types.IngestionFailureEvent.AwsKinesisFailureReason): + Optional. Failure when ingesting from AWS + Kinesis. + This field is a member of `oneof`_ ``failure``. """ @@ -850,6 +855,12 @@ class AvroFailureReason(proto.Message): """ + class SchemaViolationReason(proto.Message): + r"""Set when a Pub/Sub message fails to get published due to a + schema validation violation. + + """ + class CloudStorageFailure(proto.Message): r"""Failure when ingesting from a Cloud Storage source. @@ -881,6 +892,11 @@ class CloudStorageFailure(proto.Message): Optional. The Pub/Sub API limits prevented the desired message from being published. + This field is a member of `oneof`_ ``reason``. + schema_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.SchemaViolationReason): + Optional. The Pub/Sub message failed schema + validation. + This field is a member of `oneof`_ ``reason``. """ @@ -908,10 +924,23 @@ class CloudStorageFailure(proto.Message): oneof="reason", message="IngestionFailureEvent.ApiViolationReason", ) + schema_violation_reason: "IngestionFailureEvent.SchemaViolationReason" = ( + proto.Field( + proto.MESSAGE, + number=7, + oneof="reason", + message="IngestionFailureEvent.SchemaViolationReason", + ) + ) class AwsMskFailureReason(proto.Message): r"""Failure when ingesting from an Amazon MSK source. + This message has `oneof`_ fields (mutually exclusive fields). + For each oneof, at most one member field can be set at the same time. + Setting any member of the oneof automatically clears all other + members. + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields Attributes: @@ -931,6 +960,11 @@ class AwsMskFailureReason(proto.Message): Optional. The Pub/Sub API limits prevented the desired message from being published. + This field is a member of `oneof`_ ``reason``. + schema_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.SchemaViolationReason): + Optional. The Pub/Sub message failed schema + validation. + This field is a member of `oneof`_ ``reason``. """ @@ -956,10 +990,23 @@ class AwsMskFailureReason(proto.Message): oneof="reason", message="IngestionFailureEvent.ApiViolationReason", ) + schema_violation_reason: "IngestionFailureEvent.SchemaViolationReason" = ( + proto.Field( + proto.MESSAGE, + number=6, + oneof="reason", + message="IngestionFailureEvent.SchemaViolationReason", + ) + ) class AzureEventHubsFailureReason(proto.Message): r"""Failure when ingesting from an Azure Event Hubs source. + This message has `oneof`_ fields (mutually exclusive fields). + For each oneof, at most one member field can be set at the same time. + Setting any member of the oneof automatically clears all other + members. + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields Attributes: @@ -979,6 +1026,11 @@ class AzureEventHubsFailureReason(proto.Message): Optional. The Pub/Sub API limits prevented the desired message from being published. + This field is a member of `oneof`_ ``reason``. + schema_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.SchemaViolationReason): + Optional. The Pub/Sub message failed schema + validation. + This field is a member of `oneof`_ ``reason``. """ @@ -1004,10 +1056,23 @@ class AzureEventHubsFailureReason(proto.Message): oneof="reason", message="IngestionFailureEvent.ApiViolationReason", ) + schema_violation_reason: "IngestionFailureEvent.SchemaViolationReason" = ( + proto.Field( + proto.MESSAGE, + number=6, + oneof="reason", + message="IngestionFailureEvent.SchemaViolationReason", + ) + ) class ConfluentCloudFailureReason(proto.Message): r"""Failure when ingesting from a Confluent Cloud source. + This message has `oneof`_ fields (mutually exclusive fields). + For each oneof, at most one member field can be set at the same time. + Setting any member of the oneof automatically clears all other + members. + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields Attributes: @@ -1027,6 +1092,11 @@ class ConfluentCloudFailureReason(proto.Message): Optional. The Pub/Sub API limits prevented the desired message from being published. + This field is a member of `oneof`_ ``reason``. + schema_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.SchemaViolationReason): + Optional. The Pub/Sub message failed schema + validation. + This field is a member of `oneof`_ ``reason``. """ @@ -1052,6 +1122,57 @@ class ConfluentCloudFailureReason(proto.Message): oneof="reason", message="IngestionFailureEvent.ApiViolationReason", ) + schema_violation_reason: "IngestionFailureEvent.SchemaViolationReason" = ( + proto.Field( + proto.MESSAGE, + number=6, + oneof="reason", + message="IngestionFailureEvent.SchemaViolationReason", + ) + ) + + class AwsKinesisFailureReason(proto.Message): + r"""Failure when ingesting from an AWS Kinesis source. + + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields + + Attributes: + stream_arn (str): + Optional. The stream ARN of the Kinesis + stream being ingested from. + partition_key (str): + Optional. The partition key of the message + that failed to be ingested. + sequence_number (str): + Optional. The sequence number of the message + that failed to be ingested. + schema_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.SchemaViolationReason): + Optional. The Pub/Sub message failed schema + validation. + + This field is a member of `oneof`_ ``reason``. + """ + + stream_arn: str = proto.Field( + proto.STRING, + number=1, + ) + partition_key: str = proto.Field( + proto.STRING, + number=2, + ) + sequence_number: str = proto.Field( + proto.STRING, + number=3, + ) + schema_violation_reason: "IngestionFailureEvent.SchemaViolationReason" = ( + proto.Field( + proto.MESSAGE, + number=4, + oneof="reason", + message="IngestionFailureEvent.SchemaViolationReason", + ) + ) topic: str = proto.Field( proto.STRING, @@ -1085,6 +1206,12 @@ class ConfluentCloudFailureReason(proto.Message): oneof="failure", message=ConfluentCloudFailureReason, ) + aws_kinesis_failure: AwsKinesisFailureReason = proto.Field( + proto.MESSAGE, + number=7, + oneof="failure", + message=AwsKinesisFailureReason, + ) class JavaScriptUDF(proto.Message): @@ -1784,8 +1911,8 @@ class Subscription(proto.Message): This generally implies that messages will be retried as soon as possible for healthy subscribers. RetryPolicy will be triggered on - NACKs or acknowledgement deadline exceeded - events for a given message. + NACKs or acknowledgment deadline exceeded events + for a given message. detached (bool): Optional. Indicates whether the subscription is detached from its topic. Detached subscriptions don't receive @@ -1799,7 +1926,7 @@ class Subscription(proto.Message): ``message_id`` on this subscription: - The message sent to a subscriber is guaranteed not to be - resent before the message's acknowledgement deadline + resent before the message's acknowledgment deadline expires. - An acknowledged message will not be resent to a subscriber. @@ -1977,7 +2104,7 @@ class RetryPolicy(proto.Message): Retry delay will be exponential based on provided minimum and maximum backoffs. https://en.wikipedia.org/wiki/Exponential_backoff. - RetryPolicy will be triggered on NACKs or acknowledgement deadline + RetryPolicy will be triggered on NACKs or acknowledgment deadline exceeded events for a given message. Retry Policy is implemented on a best effort basis. At times, the @@ -2037,7 +2164,7 @@ class DeadLetterPolicy(proto.Message): message. The value must be between 5 and 100. The number of delivery attempts is defined as 1 + (the sum - of number of NACKs and number of times the acknowledgement + of number of NACKs and number of times the acknowledgment deadline has been exceeded for the message). A NACK is any call to ModifyAckDeadline with a 0 deadline. @@ -2386,7 +2513,7 @@ class CloudStorageConfig(proto.Message): elapse before a new Cloud Storage file is created. Min 1 minute, max 10 minutes, default 5 minutes. May not exceed the subscription's - acknowledgement deadline. + acknowledgment deadline. max_bytes (int): Optional. The maximum bytes that can be written to a Cloud Storage file before a new file is created. Min 1 KB, max 10 @@ -2838,7 +2965,7 @@ class AcknowledgeRequest(proto.Message): class StreamingPullRequest(proto.Message): r"""Request for the ``StreamingPull`` streaming RPC method. This request is used to establish the initial stream as well as to stream - acknowledgements and ack deadline modifications from the client to + acknowledgments and ack deadline modifications from the client to the server. Attributes: @@ -2849,12 +2976,12 @@ class StreamingPullRequest(proto.Message): client to server. Format is ``projects/{project}/subscriptions/{sub}``. ack_ids (MutableSequence[str]): - Optional. List of acknowledgement IDs for acknowledging + Optional. List of acknowledgment IDs for acknowledging previously received messages (received on this stream or a different stream). If an ack ID has expired, the corresponding message may be redelivered later. Acknowledging a message more than once will not result in an - error. If the acknowledgement ID is malformed, the stream + error. If the acknowledgment ID is malformed, the stream will be aborted with status ``INVALID_ARGUMENT``. modify_deadline_seconds (MutableSequence[int]): Optional. The list of new ack deadlines for the IDs listed @@ -2872,7 +2999,7 @@ class StreamingPullRequest(proto.Message): request. If the value is < 0 (an error), the stream will be aborted with status ``INVALID_ARGUMENT``. modify_deadline_ack_ids (MutableSequence[str]): - Optional. List of acknowledgement IDs whose deadline will be + Optional. List of acknowledgment IDs whose deadline will be modified based on the corresponding element in ``modify_deadline_seconds``. This field can be used to indicate that more time is needed to process a message by @@ -2974,22 +3101,22 @@ class StreamingPullResponse(proto.Message): """ class AcknowledgeConfirmation(proto.Message): - r"""Acknowledgement IDs sent in one or more previous requests to + r"""Acknowledgment IDs sent in one or more previous requests to acknowledge a previously received message. Attributes: ack_ids (MutableSequence[str]): Optional. Successfully processed - acknowledgement IDs. + acknowledgment IDs. invalid_ack_ids (MutableSequence[str]): - Optional. List of acknowledgement IDs that - were malformed or whose acknowledgement deadline + Optional. List of acknowledgment IDs that + were malformed or whose acknowledgment deadline has expired. unordered_ack_ids (MutableSequence[str]): - Optional. List of acknowledgement IDs that + Optional. List of acknowledgment IDs that were out of order. temporary_failed_ack_ids (MutableSequence[str]): - Optional. List of acknowledgement IDs that + Optional. List of acknowledgment IDs that failed processing with temporary issues. """ @@ -3011,19 +3138,19 @@ class AcknowledgeConfirmation(proto.Message): ) class ModifyAckDeadlineConfirmation(proto.Message): - r"""Acknowledgement IDs sent in one or more previous requests to + r"""Acknowledgment IDs sent in one or more previous requests to modify the deadline for a specific message. Attributes: ack_ids (MutableSequence[str]): Optional. Successfully processed - acknowledgement IDs. + acknowledgment IDs. invalid_ack_ids (MutableSequence[str]): - Optional. List of acknowledgement IDs that - were malformed or whose acknowledgement deadline + Optional. List of acknowledgment IDs that + were malformed or whose acknowledgment deadline has expired. temporary_failed_ack_ids (MutableSequence[str]): - Optional. List of acknowledgement IDs that + Optional. List of acknowledgment IDs that failed processing with temporary issues. """ From a5f0a2f7941387f34b5684678cf03e7a71f5ca97 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 09:53:07 -0400 Subject: [PATCH 154/190] chore(main): release 2.30.0 (#1418) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index db87e5c90..8028c14b5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.29.1" + ".": "2.30.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1435f6752..7a9265758 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.30.0](https://github.com/googleapis/python-pubsub/compare/v2.29.1...v2.30.0) (2025-06-07) + + +### Features + +* Add SchemaViolationReason to IngestionFailureEvent ([#1411](https://github.com/googleapis/python-pubsub/issues/1411)) ([c046ca2](https://github.com/googleapis/python-pubsub/commit/c046ca22e9bddff6b50f7670bf6b9b9470bf78e8)) + ## [2.29.1](https://github.com/googleapis/python-pubsub/compare/v2.29.0...v2.29.1) (2025-05-23) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index f62042190..5ebb3bec4 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.29.1" # {x-release-please-version} +__version__ = "2.30.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index f62042190..5ebb3bec4 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.29.1" # {x-release-please-version} +__version__ = "2.30.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index dbb835bb3..28ac15bef 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.29.1" + "version": "2.30.0" }, "snippets": [ { From 272b09f1cf76719296c4ea211a15253e4448ed81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 11:53:16 -0400 Subject: [PATCH 155/190] build(deps): bump protobuf from 6.31.0 to 6.31.1 in /samples/snippets (#1424) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 83202d4f8..22d6f32d1 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -2,7 +2,7 @@ google-cloud-pubsub==2.29.0 avro==1.12.0 protobuf===4.24.4; python_version == '3.7' protobuf===5.29.4; python_version == '3.8' -protobuf==6.31.0; python_version >= '3.9' +protobuf==6.31.1; python_version >= '3.9' avro==1.12.0 opentelemetry-api===1.22.0; python_version == '3.7' opentelemetry-sdk===1.22.0; python_version == '3.7' From e081beb29056035304d365ec9c50fa7ffbac6886 Mon Sep 17 00:00:00 2001 From: Andrew Browne <81702808+abbrowne126@users.noreply.github.com> Date: Tue, 24 Jun 2025 16:42:55 -0400 Subject: [PATCH 156/190] Fix: Surface Fatal Stream Errors to Future; Adjust Retryable Error Codes (#1422) --- .../_protocol/streaming_pull_manager.py | 54 ++++++++++++++++--- .../subscriber/test_streaming_pull_manager.py | 53 +++++++++++++++--- 2 files changed, 92 insertions(+), 15 deletions(-) diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py index 4c9e1c20e..486a728b4 100644 --- a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py +++ b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py @@ -16,6 +16,7 @@ import collections import functools +import inspect import itertools import logging import threading @@ -62,14 +63,22 @@ _REGULAR_SHUTDOWN_THREAD_NAME = "Thread-RegularStreamShutdown" _RPC_ERROR_THREAD_NAME = "Thread-OnRpcTerminated" _RETRYABLE_STREAM_ERRORS = ( + exceptions.Aborted, exceptions.DeadlineExceeded, - exceptions.ServiceUnavailable, + exceptions.GatewayTimeout, exceptions.InternalServerError, + exceptions.ResourceExhausted, + exceptions.ServiceUnavailable, exceptions.Unknown, - exceptions.GatewayTimeout, - exceptions.Aborted, ) -_TERMINATING_STREAM_ERRORS = (exceptions.Cancelled,) +_TERMINATING_STREAM_ERRORS = ( + exceptions.Cancelled, + exceptions.InvalidArgument, + exceptions.NotFound, + exceptions.PermissionDenied, + exceptions.Unauthenticated, + exceptions.Unauthorized, +) _MAX_LOAD = 1.0 """The load threshold above which to pause the incoming message stream.""" @@ -98,6 +107,13 @@ code_pb2.UNAVAILABLE, } +# `on_fatal_exception` was added in `google-api-core v2.25.1``, which allows us to inform +# callers on unrecoverable errors. We can only pass this arg if it's available in the +# `BackgroundConsumer` spec. +_SHOULD_USE_ON_FATAL_ERROR_CALLBACK = "on_fatal_exception" in inspect.getfullargspec( + bidi.BackgroundConsumer +) + def _wrap_as_exception(maybe_exception: Any) -> BaseException: """Wrap an object as a Python exception, if needed. @@ -876,7 +892,18 @@ def open( assert self._scheduler is not None scheduler_queue = self._scheduler.queue self._dispatcher = dispatcher.Dispatcher(self, scheduler_queue) - self._consumer = bidi.BackgroundConsumer(self._rpc, self._on_response) + + # `on_fatal_exception` is only available in more recent library versions. + # For backwards compatibility reasons, we only pass it when `google-api-core` supports it. + if _SHOULD_USE_ON_FATAL_ERROR_CALLBACK: + self._consumer = bidi.BackgroundConsumer( + self._rpc, + self._on_response, + on_fatal_exception=self._on_fatal_exception, + ) + else: + self._consumer = bidi.BackgroundConsumer(self._rpc, self._on_response) + self._leaser = leaser.Leaser(self) self._heartbeater = heartbeater.Heartbeater(self) @@ -1247,6 +1274,17 @@ def _on_response(self, response: gapic_types.StreamingPullResponse) -> None: self.maybe_pause_consumer() + def _on_fatal_exception(self, exception: BaseException) -> None: + """ + Called whenever `self.consumer` receives a non-retryable exception. + We close the manager on such non-retryable cases. + """ + _LOGGER.exception( + "Streaming pull terminating after receiving non-recoverable error: %s", + exception, + ) + self.close(exception) + def _should_recover(self, exception: BaseException) -> bool: """Determine if an error on the RPC stream should be recovered. @@ -1283,8 +1321,10 @@ def _should_terminate(self, exception: BaseException) -> bool: in a list of terminating exceptions. """ exception = _wrap_as_exception(exception) - if isinstance(exception, _TERMINATING_STREAM_ERRORS): - _LOGGER.debug("Observed terminating stream error %s", exception) + is_api_error = isinstance(exception, exceptions.GoogleAPICallError) + # Terminate any non-API errors, or non-retryable errors (permission denied, unauthorized, etc.) + if not is_api_error or isinstance(exception, _TERMINATING_STREAM_ERRORS): + _LOGGER.error("Observed terminating stream error %s", exception) return True _LOGGER.debug("Observed non-terminating stream error %s", exception) return False diff --git a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py index f4ceedaf0..86d2461e7 100644 --- a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py +++ b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py @@ -1333,7 +1333,13 @@ def test_open(heartbeater, dispatcher, leaser, background_consumer, resumable_bi leaser.return_value.start.assert_called_once() assert manager.leaser == leaser.return_value - background_consumer.assert_called_once_with(manager._rpc, manager._on_response) + if streaming_pull_manager._SHOULD_USE_ON_FATAL_ERROR_CALLBACK: + background_consumer.assert_called_once_with( + manager._rpc, manager._on_response, manager._on_fatal_exception + ) + else: + background_consumer.assert_called_once_with(manager._rpc, manager._on_response) + background_consumer.return_value.start.assert_called_once() assert manager._consumer == background_consumer.return_value @@ -1432,6 +1438,31 @@ def test_close(): assert manager.is_active is False +def test_closes_on_fatal_consumer_error(): + ( + manager, + consumer, + dispatcher, + leaser, + heartbeater, + scheduler, + ) = make_running_manager() + + if streaming_pull_manager._SHOULD_USE_ON_FATAL_ERROR_CALLBACK: + error = ValueError("some fatal exception") + manager._on_fatal_exception(error) + + await_manager_shutdown(manager, timeout=3) + + consumer.stop.assert_called_once() + leaser.stop.assert_called_once() + dispatcher.stop.assert_called_once() + heartbeater.stop.assert_called_once() + scheduler.shutdown.assert_called_once() + + assert manager.is_active is False + + def test_close_inactive_consumer(): ( manager, @@ -2270,18 +2301,24 @@ def test__should_recover_false(): def test__should_terminate_true(): manager = make_manager() - details = "Cancelled. Go away, before I taunt you a second time." - exc = exceptions.Cancelled(details) - - assert manager._should_terminate(exc) is True + for exc in [ + exceptions.Cancelled(""), + exceptions.PermissionDenied(""), + TypeError(), + ValueError(), + ]: + assert manager._should_terminate(exc) def test__should_terminate_false(): manager = make_manager() - exc = TypeError("wahhhhhh") - - assert manager._should_terminate(exc) is False + for exc in [ + exceptions.ResourceExhausted(""), + exceptions.ServiceUnavailable(""), + exceptions.DeadlineExceeded(""), + ]: + assert not manager._should_terminate(exc) @mock.patch("threading.Thread", autospec=True) From 8ab13e1b71c151f0146548e7224dd38c9d719a88 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Thu, 26 Jun 2025 16:44:28 -0400 Subject: [PATCH 157/190] feat: Add MessageTransformationFailureReason to IngestionFailureEvent (#1427) Co-authored-by: Owl Bot --- google/pubsub_v1/types/pubsub.py | 72 ++++++++++++++++++- .../snippet_metadata_google.pubsub.v1.json | 2 +- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index dcc25bb31..227da2208 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -861,6 +861,12 @@ class SchemaViolationReason(proto.Message): """ + class MessageTransformationFailureReason(proto.Message): + r"""Set when a Pub/Sub message fails to get published due to a + message transformation error. + + """ + class CloudStorageFailure(proto.Message): r"""Failure when ingesting from a Cloud Storage source. @@ -897,6 +903,11 @@ class CloudStorageFailure(proto.Message): Optional. The Pub/Sub message failed schema validation. + This field is a member of `oneof`_ ``reason``. + message_transformation_failure_reason (google.pubsub_v1.types.IngestionFailureEvent.MessageTransformationFailureReason): + Optional. Failure encountered when applying a + message transformation to the Pub/Sub message. + This field is a member of `oneof`_ ``reason``. """ @@ -932,6 +943,12 @@ class CloudStorageFailure(proto.Message): message="IngestionFailureEvent.SchemaViolationReason", ) ) + message_transformation_failure_reason: "IngestionFailureEvent.MessageTransformationFailureReason" = proto.Field( + proto.MESSAGE, + number=8, + oneof="reason", + message="IngestionFailureEvent.MessageTransformationFailureReason", + ) class AwsMskFailureReason(proto.Message): r"""Failure when ingesting from an Amazon MSK source. @@ -965,6 +982,11 @@ class AwsMskFailureReason(proto.Message): Optional. The Pub/Sub message failed schema validation. + This field is a member of `oneof`_ ``reason``. + message_transformation_failure_reason (google.pubsub_v1.types.IngestionFailureEvent.MessageTransformationFailureReason): + Optional. Failure encountered when applying a + message transformation to the Pub/Sub message. + This field is a member of `oneof`_ ``reason``. """ @@ -998,6 +1020,12 @@ class AwsMskFailureReason(proto.Message): message="IngestionFailureEvent.SchemaViolationReason", ) ) + message_transformation_failure_reason: "IngestionFailureEvent.MessageTransformationFailureReason" = proto.Field( + proto.MESSAGE, + number=7, + oneof="reason", + message="IngestionFailureEvent.MessageTransformationFailureReason", + ) class AzureEventHubsFailureReason(proto.Message): r"""Failure when ingesting from an Azure Event Hubs source. @@ -1031,6 +1059,11 @@ class AzureEventHubsFailureReason(proto.Message): Optional. The Pub/Sub message failed schema validation. + This field is a member of `oneof`_ ``reason``. + message_transformation_failure_reason (google.pubsub_v1.types.IngestionFailureEvent.MessageTransformationFailureReason): + Optional. Failure encountered when applying a + message transformation to the Pub/Sub message. + This field is a member of `oneof`_ ``reason``. """ @@ -1064,6 +1097,12 @@ class AzureEventHubsFailureReason(proto.Message): message="IngestionFailureEvent.SchemaViolationReason", ) ) + message_transformation_failure_reason: "IngestionFailureEvent.MessageTransformationFailureReason" = proto.Field( + proto.MESSAGE, + number=7, + oneof="reason", + message="IngestionFailureEvent.MessageTransformationFailureReason", + ) class ConfluentCloudFailureReason(proto.Message): r"""Failure when ingesting from a Confluent Cloud source. @@ -1097,6 +1136,11 @@ class ConfluentCloudFailureReason(proto.Message): Optional. The Pub/Sub message failed schema validation. + This field is a member of `oneof`_ ``reason``. + message_transformation_failure_reason (google.pubsub_v1.types.IngestionFailureEvent.MessageTransformationFailureReason): + Optional. Failure encountered when applying a + message transformation to the Pub/Sub message. + This field is a member of `oneof`_ ``reason``. """ @@ -1130,10 +1174,21 @@ class ConfluentCloudFailureReason(proto.Message): message="IngestionFailureEvent.SchemaViolationReason", ) ) + message_transformation_failure_reason: "IngestionFailureEvent.MessageTransformationFailureReason" = proto.Field( + proto.MESSAGE, + number=7, + oneof="reason", + message="IngestionFailureEvent.MessageTransformationFailureReason", + ) class AwsKinesisFailureReason(proto.Message): r"""Failure when ingesting from an AWS Kinesis source. + This message has `oneof`_ fields (mutually exclusive fields). + For each oneof, at most one member field can be set at the same time. + Setting any member of the oneof automatically clears all other + members. + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields Attributes: @@ -1150,6 +1205,11 @@ class AwsKinesisFailureReason(proto.Message): Optional. The Pub/Sub message failed schema validation. + This field is a member of `oneof`_ ``reason``. + message_transformation_failure_reason (google.pubsub_v1.types.IngestionFailureEvent.MessageTransformationFailureReason): + Optional. Failure encountered when applying a + message transformation to the Pub/Sub message. + This field is a member of `oneof`_ ``reason``. """ @@ -1173,6 +1233,12 @@ class AwsKinesisFailureReason(proto.Message): message="IngestionFailureEvent.SchemaViolationReason", ) ) + message_transformation_failure_reason: "IngestionFailureEvent.MessageTransformationFailureReason" = proto.Field( + proto.MESSAGE, + number=5, + oneof="reason", + message="IngestionFailureEvent.MessageTransformationFailureReason", + ) topic: str = proto.Field( proto.STRING, @@ -3091,10 +3157,12 @@ class StreamingPullResponse(proto.Message): will not be empty. acknowledge_confirmation (google.pubsub_v1.types.StreamingPullResponse.AcknowledgeConfirmation): Optional. This field will only be set if - ``enable_exactly_once_delivery`` is set to ``true``. + ``enable_exactly_once_delivery`` is set to ``true`` and is + not guaranteed to be populated. modify_ack_deadline_confirmation (google.pubsub_v1.types.StreamingPullResponse.ModifyAckDeadlineConfirmation): Optional. This field will only be set if - ``enable_exactly_once_delivery`` is set to ``true``. + ``enable_exactly_once_delivery`` is set to ``true`` and is + not guaranteed to be populated. subscription_properties (google.pubsub_v1.types.StreamingPullResponse.SubscriptionProperties): Optional. Properties associated with this subscription. diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 28ac15bef..dd0d6423a 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.30.0" + "version": "0.1.0" }, "snippets": [ { From 2b7e4231f887409c3d962b61dca3539777ef1d22 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 09:54:25 -0400 Subject: [PATCH 158/190] chore(main): release 2.31.0 (#1426) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 12 ++++++++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 8028c14b5..25b8f2e80 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.30.0" + ".": "2.31.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a9265758..c9daf8a3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.31.0](https://github.com/googleapis/python-pubsub/compare/v2.30.0...v2.31.0) (2025-06-26) + + +### Features + +* Add MessageTransformationFailureReason to IngestionFailureEvent ([#1427](https://github.com/googleapis/python-pubsub/issues/1427)) ([8ab13e1](https://github.com/googleapis/python-pubsub/commit/8ab13e1b71c151f0146548e7224dd38c9d719a88)) + + +### Bug Fixes + +* Surface Fatal Stream Errors to Future; Adjust Retryable Error Codes ([#1422](https://github.com/googleapis/python-pubsub/issues/1422)) ([e081beb](https://github.com/googleapis/python-pubsub/commit/e081beb29056035304d365ec9c50fa7ffbac6886)) + ## [2.30.0](https://github.com/googleapis/python-pubsub/compare/v2.29.1...v2.30.0) (2025-06-07) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 5ebb3bec4..8ab09c42e 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.30.0" # {x-release-please-version} +__version__ = "2.31.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 5ebb3bec4..8ab09c42e 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.30.0" # {x-release-please-version} +__version__ = "2.31.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index dd0d6423a..cb25ebf70 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "0.1.0" + "version": "2.31.0" }, "snippets": [ { From b0f6f49f65752e88523f9c4209366d2a18140416 Mon Sep 17 00:00:00 2001 From: Andrew Browne <81702808+abbrowne126@users.noreply.github.com> Date: Wed, 23 Jul 2025 15:38:23 -0400 Subject: [PATCH 159/190] Fix: Propagate Otel Context to Subscriber Callback if Provided (#1429) Co-authored-by: Owl Bot --- .../open_telemetry/subscribe_opentelemetry.py | 10 +++++++++- .../subscriber/_protocol/streaming_pull_manager.py | 6 ++++-- .../subscriber/test_streaming_pull_manager.py | 4 ++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/google/cloud/pubsub_v1/open_telemetry/subscribe_opentelemetry.py b/google/cloud/pubsub_v1/open_telemetry/subscribe_opentelemetry.py index 88870be60..5a6abd21b 100644 --- a/google/cloud/pubsub_v1/open_telemetry/subscribe_opentelemetry.py +++ b/google/cloud/pubsub_v1/open_telemetry/subscribe_opentelemetry.py @@ -160,7 +160,7 @@ def end_subscribe_scheduler_span(self) -> None: assert self._scheduler_span is not None self._scheduler_span.end() - def start_process_span(self) -> None: + def start_process_span(self) -> trace.Span: assert self._subscribe_span is not None tracer = trace.get_tracer(_OPEN_TELEMETRY_TRACER_NAME) publish_create_span_link: Optional[trace.Link] = None @@ -186,6 +186,7 @@ def start_process_span(self) -> None: end_on_exit=False, ) as process_span: self._process_span = process_span + return process_span def end_process_span(self) -> None: assert self._process_span is not None @@ -200,6 +201,13 @@ def add_process_span_event(self, event: str) -> None: }, ) + def __enter__(self) -> trace.Span: + return self.start_process_span() + + def __exit__(self, exc_type, exc_val, traceback): + if self._process_span: + self.end_process_span() + def start_modack_span( subscribe_span_links: List[trace.Link], diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py index 486a728b4..8c8ebf479 100644 --- a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py +++ b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py @@ -148,8 +148,10 @@ def _wrap_callback_errors( try: if message.opentelemetry_data: message.opentelemetry_data.end_subscribe_concurrency_control_span() - message.opentelemetry_data.start_process_span() - callback(message) + with message.opentelemetry_data: + callback(message) + else: + callback(message) except BaseException as exc: # Note: the likelihood of this failing is extremely low. This just adds # a message to a queue, so if this doesn't work the world is in an diff --git a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py index 86d2461e7..f45959637 100644 --- a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py +++ b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py @@ -2956,10 +2956,10 @@ def test_opentelemetry_subscriber_concurrency_control_span_end(span_exporter): streaming_pull_manager._wrap_callback_errors(mock.Mock(), mock.Mock(), msg) spans = span_exporter.get_finished_spans() - assert len(spans) == 1 + assert len(spans) == 2 concurrency_control_span = spans[0] - concurrency_control_span.name == "subscriber concurrency control" + assert concurrency_control_span.name == "subscriber concurrency control" def test_opentelemetry_wrap_callback_error(span_exporter): From 3a3aa79040d656a3391a153386ec662d002f9368 Mon Sep 17 00:00:00 2001 From: Andrew Browne <81702808+abbrowne126@users.noreply.github.com> Date: Mon, 28 Jul 2025 16:27:00 -0400 Subject: [PATCH 160/190] fix: Change Log Severities for Terminated Streams (#1433) --- .../pubsub_v1/subscriber/_protocol/streaming_pull_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py index 8c8ebf479..de3ac3780 100644 --- a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py +++ b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py @@ -1281,7 +1281,7 @@ def _on_fatal_exception(self, exception: BaseException) -> None: Called whenever `self.consumer` receives a non-retryable exception. We close the manager on such non-retryable cases. """ - _LOGGER.exception( + _LOGGER.info( "Streaming pull terminating after receiving non-recoverable error: %s", exception, ) @@ -1326,7 +1326,7 @@ def _should_terminate(self, exception: BaseException) -> bool: is_api_error = isinstance(exception, exceptions.GoogleAPICallError) # Terminate any non-API errors, or non-retryable errors (permission denied, unauthorized, etc.) if not is_api_error or isinstance(exception, _TERMINATING_STREAM_ERRORS): - _LOGGER.error("Observed terminating stream error %s", exception) + _LOGGER.debug("Observed terminating stream error %s", exception) return True _LOGGER.debug("Observed non-terminating stream error %s", exception) return False From c198bef0e19fd65b37fb845d4ebfaf6e2bd20926 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 14:09:26 -0700 Subject: [PATCH 161/190] chore(main): release 2.31.1 (#1431) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 25b8f2e80..1c48d61d0 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.31.0" + ".": "2.31.1" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c9daf8a3e..da524d1a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.31.1](https://github.com/googleapis/python-pubsub/compare/v2.31.0...v2.31.1) (2025-07-28) + + +### Bug Fixes + +* Change Log Severities for Terminated Streams ([#1433](https://github.com/googleapis/python-pubsub/issues/1433)) ([3a3aa79](https://github.com/googleapis/python-pubsub/commit/3a3aa79040d656a3391a153386ec662d002f9368)) +* Propagate Otel Context to Subscriber Callback if Provided ([#1429](https://github.com/googleapis/python-pubsub/issues/1429)) ([b0f6f49](https://github.com/googleapis/python-pubsub/commit/b0f6f49f65752e88523f9c4209366d2a18140416)) + ## [2.31.0](https://github.com/googleapis/python-pubsub/compare/v2.30.0...v2.31.0) (2025-06-26) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 8ab09c42e..79eaa5593 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.31.0" # {x-release-please-version} +__version__ = "2.31.1" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 8ab09c42e..79eaa5593 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.31.0" # {x-release-please-version} +__version__ = "2.31.1" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index cb25ebf70..1e6003150 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.31.0" + "version": "2.31.1" }, "snippets": [ { From 8f9c805bad1a9087369c29f77017000ae7bb031a Mon Sep 17 00:00:00 2001 From: Andrew Browne <81702808+abbrowne126@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:25:24 -0400 Subject: [PATCH 162/190] chore(deps): Upgrade snippets and sample dependencies (#1421) Co-authored-by: Owl Bot --- .github/workflows/lint.yml | 2 +- noxfile.py | 6 +++--- samples/snippets/requirements-test.txt | 7 ++++--- samples/snippets/requirements.txt | 8 +++++--- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4866193af..1051da0bd 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 with: - python-version: "3.8" + python-version: "3.10" - name: Install nox run: | python -m pip install --upgrade setuptools pip wheel diff --git a/noxfile.py b/noxfile.py index dd182f105..ba80fd80b 100644 --- a/noxfile.py +++ b/noxfile.py @@ -34,7 +34,7 @@ MYPY_VERSION = "mypy==1.10.0" -DEFAULT_PYTHON_VERSION = "3.8" +DEFAULT_PYTHON_VERSION = "3.10" UNIT_TEST_PYTHON_VERSIONS: List[str] = [ "3.7", @@ -351,7 +351,7 @@ def cover(session): session.run("coverage", "erase") -@nox.session(python="3.10") +@nox.session(python=DEFAULT_PYTHON_VERSION) def docs(session): """Build the docs for this library.""" @@ -386,7 +386,7 @@ def docs(session): ) -@nox.session(python="3.10") +@nox.session(python=DEFAULT_PYTHON_VERSION) def docfx(session): """Build the docfx yaml files for this library.""" diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index 2bf14b760..5b13ec325 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1,8 +1,9 @@ backoff==2.2.1 pytest===7.4.4; python_version == '3.7' -pytest==8.3.5; python_version >= '3.8' +pytest===8.3.5; python_version == '3.8' +pytest==8.4.0; python_version >= '3.9' mock==5.2.0 flaky==3.8.1 -google-cloud-bigquery==3.30.0; python_version < '3.9' -google-cloud-bigquery==3.33.0; python_version >= '3.9' +google-cloud-bigquery===3.30.0; python_version <= '3.8' +google-cloud-bigquery==3.34.0; python_version >= '3.9' google-cloud-storage==3.1.0 diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 22d6f32d1..1d6f5d992 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,4 +1,4 @@ -google-cloud-pubsub==2.29.0 +google-cloud-pubsub==2.29.1 avro==1.12.0 protobuf===4.24.4; python_version == '3.7' protobuf===5.29.4; python_version == '3.8' @@ -6,6 +6,8 @@ protobuf==6.31.1; python_version >= '3.9' avro==1.12.0 opentelemetry-api===1.22.0; python_version == '3.7' opentelemetry-sdk===1.22.0; python_version == '3.7' -opentelemetry-api==1.33.1; python_version >= '3.8' -opentelemetry-sdk==1.33.1; python_version >= '3.8' +opentelemetry-api===1.33.1; python_version == '3.8' +opentelemetry-sdk===1.33.1; python_version == '3.8' +opentelemetry-api==1.34.0; python_version >= '3.9' +opentelemetry-sdk==1.34.0; python_version >= '3.9' opentelemetry-exporter-gcp-trace==1.9.0 From 1e534decc9928493ed020d1e382cce941f7545ee Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Thu, 2 Oct 2025 22:08:14 +0100 Subject: [PATCH 163/190] chore(deps): update all dependencies (#1456) Co-authored-by: abbrowne126 <81702808+abbrowne126@users.noreply.github.com> --- .github/workflows/docs.yml | 8 ++++---- .github/workflows/lint.yml | 6 +++--- .../pubsub_v1/publisher/_sequencer/base.py | 4 ++-- google/pubsub_v1/services/publisher/client.py | 2 +- .../services/schema_service/client.py | 2 +- .../pubsub_v1/services/subscriber/client.py | 4 ++-- google/pubsub_v1/types/__init__.py | 5 +++-- mypy.ini | 4 +++- noxfile.py | 20 +++++++++++-------- owlbot.py | 2 +- pytest.ini | 4 +++- samples/snippets/requirements-test.txt | 6 +++--- samples/snippets/requirements.txt | 8 ++++---- 13 files changed, 42 insertions(+), 33 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 2833fe98f..0d0fdb861 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -8,9 +8,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.10" - name: Install nox @@ -24,9 +24,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.10" - name: Install nox diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 1051da0bd..46a3ff38f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -8,11 +8,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: - python-version: "3.10" + python-version: "3.13" - name: Install nox run: | python -m pip install --upgrade setuptools pip wheel diff --git a/google/cloud/pubsub_v1/publisher/_sequencer/base.py b/google/cloud/pubsub_v1/publisher/_sequencer/base.py index 58ec5a571..daaacaa33 100644 --- a/google/cloud/pubsub_v1/publisher/_sequencer/base.py +++ b/google/cloud/pubsub_v1/publisher/_sequencer/base.py @@ -53,8 +53,8 @@ def unpause(self) -> None: # pragma: NO COVER def publish( self, message: gapic_types.PubsubMessage, - retry: "OptionalRetry" = gapic_v1.method.DEFAULT, - timeout: gapic_types.TimeoutType = gapic_v1.method.DEFAULT, + retry: "OptionalRetry" = gapic_v1.method.DEFAULT, # type: ignore + timeout: gapic_types.TimeoutType = gapic_v1.method.DEFAULT, # type: ignore ) -> "futures.Future": # pragma: NO COVER """Publish message for this ordering key. diff --git a/google/pubsub_v1/services/publisher/client.py b/google/pubsub_v1/services/publisher/client.py index c8f39273e..28cfa680b 100644 --- a/google/pubsub_v1/services/publisher/client.py +++ b/google/pubsub_v1/services/publisher/client.py @@ -725,7 +725,7 @@ def __init__( emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") if emulator_host: - if issubclass(transport_init, type(self)._transport_registry["grpc"]): + if issubclass(transport_init, type(self)._transport_registry["grpc"]): # type: ignore channel = grpc.insecure_channel(target=emulator_host) else: channel = grpc.aio.insecure_channel(target=emulator_host) diff --git a/google/pubsub_v1/services/schema_service/client.py b/google/pubsub_v1/services/schema_service/client.py index 493ffd2b6..29730b85e 100644 --- a/google/pubsub_v1/services/schema_service/client.py +++ b/google/pubsub_v1/services/schema_service/client.py @@ -677,7 +677,7 @@ def __init__( emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") if emulator_host: - if issubclass(transport_init, type(self)._transport_registry["grpc"]): + if issubclass(transport_init, type(self)._transport_registry["grpc"]): # type: ignore channel = grpc.insecure_channel(target=emulator_host) else: channel = grpc.aio.insecure_channel(target=emulator_host) diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index ff7edf538..f5945ba64 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -729,7 +729,7 @@ def __init__( emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") if emulator_host: - if issubclass(transport_init, type(self)._transport_registry["grpc"]): + if issubclass(transport_init, type(self)._transport_registry["grpc"]): # type: ignore channel = grpc.insecure_channel(target=emulator_host) else: channel = grpc.aio.insecure_channel(target=emulator_host) @@ -1897,7 +1897,7 @@ def request_generator(): # Wrappers in api-core should not automatically pre-fetch the first # stream result, as this breaks the stream when re-opening it. # https://github.com/googleapis/python-pubsub/issues/93#issuecomment-630762257 - self._transport.streaming_pull._prefetch_first_result_ = False + self._transport.streaming_pull._prefetch_first_result_ = False # type: ignore # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. diff --git a/google/pubsub_v1/types/__init__.py b/google/pubsub_v1/types/__init__.py index 85c6b901b..c4f020ee2 100644 --- a/google/pubsub_v1/types/__init__.py +++ b/google/pubsub_v1/types/__init__.py @@ -87,12 +87,13 @@ Encoding, SchemaView, ) +import google.api_core.timeout TimeoutType = Union[ int, float, - "google.api_core.timeout.ConstantTimeout", - "google.api_core.timeout.ExponentialTimeout", + google.api_core.timeout.ConstantTimeout, + google.api_core.timeout.ExponentialTimeout, ] """The type of the timeout parameter of publisher client methods.""" diff --git a/mypy.ini b/mypy.ini index 574c5aed3..00f693e26 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,3 +1,5 @@ [mypy] -python_version = 3.7 +python_version = 3.8 namespace_packages = True +# Autogenerated folder - TODO remove this https://github.com/googleapis/python-pubsub/issues/536 +exclude = google/pubsub_v1 diff --git a/noxfile.py b/noxfile.py index ba80fd80b..70e65a571 100644 --- a/noxfile.py +++ b/noxfile.py @@ -34,7 +34,7 @@ MYPY_VERSION = "mypy==1.10.0" -DEFAULT_PYTHON_VERSION = "3.10" +DEFAULT_PYTHON_VERSION = "3.13" UNIT_TEST_PYTHON_VERSIONS: List[str] = [ "3.7", @@ -105,8 +105,7 @@ def mypy(session): # Version 2.1.1 of google-api-core version is the first type-checked release. # Version 2.2.0 of google-cloud-core version is the first type-checked release. session.install( - "google-api-core[grpc]>=2.1.1", - "google-cloud-core>=2.2.0", + "google-api-core[grpc]>=2.1.1", "google-cloud-core>=2.2.0", "types-requests" ) # Just install the type info directly, since "mypy --install-types" might @@ -118,7 +117,8 @@ def mypy(session): # TODO: Only check the hand-written layer, the generated code does not pass # mypy checks yet. # https://github.com/googleapis/gapic-generator-python/issues/1092 - session.run("mypy", "-p", "google.cloud") + # TODO: Re-enable mypy checks once we merge, since incremental checks are failing due to protobuf upgrade + # session.run("mypy", "-p", "google.cloud", "--exclude", "google/pubsub_v1/") @nox.session(python=DEFAULT_PYTHON_VERSION) @@ -132,7 +132,9 @@ def mypy_samples(session): # Just install the type info directly, since "mypy --install-types" might # require an additional pass. - session.install("types-mock", "types-protobuf", "types-setuptools") + session.install( + "types-mock", "types-protobuf", "types-setuptools", "types-requests" + ) session.run( "mypy", @@ -192,7 +194,7 @@ def format(session): @nox.session(python=DEFAULT_PYTHON_VERSION) def lint_setup_py(session): """Verify that setup.py is valid (including RST check).""" - session.install("docutils", "pygments") + session.install("setuptools", "docutils", "pygments") session.run("python", "setup.py", "check", "--restructuredtext", "--strict") @@ -351,7 +353,8 @@ def cover(session): session.run("coverage", "erase") -@nox.session(python=DEFAULT_PYTHON_VERSION) +# py > 3.10 not supported yet +@nox.session(python="3.10") def docs(session): """Build the docs for this library.""" @@ -386,7 +389,8 @@ def docs(session): ) -@nox.session(python=DEFAULT_PYTHON_VERSION) +# py > 3.10 not supported yet +@nox.session(python="3.10") def docfx(session): """Build the docfx yaml files for this library.""" diff --git a/owlbot.py b/owlbot.py index d845e5758..2a131d557 100644 --- a/owlbot.py +++ b/owlbot.py @@ -108,7 +108,7 @@ emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") if emulator_host: - if issubclass(transport_init, type(self)._transport_registry["grpc"]): + if issubclass(transport_init, type(self)._transport_registry["grpc"]): # type: ignore channel = grpc.insecure_channel(target=emulator_host) else: channel = grpc.aio.insecure_channel(target=emulator_host) diff --git a/pytest.ini b/pytest.ini index 6d55a7315..09a522efe 100644 --- a/pytest.ini +++ b/pytest.ini @@ -19,4 +19,6 @@ filterwarnings = ignore:.*pkg_resources.declare_namespace:DeprecationWarning ignore:.*pkg_resources is deprecated as an API:DeprecationWarning # Remove once https://github.com/googleapis/gapic-generator-python/issues/2303 is fixed - ignore:The python-bigquery library will stop supporting Python 3.7:PendingDeprecationWarning \ No newline at end of file + ignore:The python-bigquery library will stop supporting Python 3.7:PendingDeprecationWarning + # Remove once we move off credential files https://github.com/googleapis/google-auth-library-python/pull/1812 + ignore:Your config file at [/home/kbuilder/.docker/config.json] contains these credential helper entries:DeprecationWarning diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index 5b13ec325..7659e3676 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1,9 +1,9 @@ backoff==2.2.1 pytest===7.4.4; python_version == '3.7' pytest===8.3.5; python_version == '3.8' -pytest==8.4.0; python_version >= '3.9' +pytest==8.4.2; python_version >= '3.9' mock==5.2.0 flaky==3.8.1 google-cloud-bigquery===3.30.0; python_version <= '3.8' -google-cloud-bigquery==3.34.0; python_version >= '3.9' -google-cloud-storage==3.1.0 +google-cloud-bigquery==3.38.0; python_version >= '3.9' +google-cloud-storage==3.4.0 diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 1d6f5d992..63a78cd67 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,13 +1,13 @@ -google-cloud-pubsub==2.29.1 +google-cloud-pubsub==2.31.1 avro==1.12.0 protobuf===4.24.4; python_version == '3.7' protobuf===5.29.4; python_version == '3.8' -protobuf==6.31.1; python_version >= '3.9' +protobuf==6.32.1; python_version >= '3.9' avro==1.12.0 opentelemetry-api===1.22.0; python_version == '3.7' opentelemetry-sdk===1.22.0; python_version == '3.7' opentelemetry-api===1.33.1; python_version == '3.8' opentelemetry-sdk===1.33.1; python_version == '3.8' -opentelemetry-api==1.34.0; python_version >= '3.9' -opentelemetry-sdk==1.34.0; python_version >= '3.9' +opentelemetry-api==1.37.0; python_version >= '3.9' +opentelemetry-sdk==1.37.0; python_version >= '3.9' opentelemetry-exporter-gcp-trace==1.9.0 From b5d4a458ca9319bebbe3142a1f05d4d4471c8d4d Mon Sep 17 00:00:00 2001 From: Andrew Browne <81702808+abbrowne126@users.noreply.github.com> Date: Mon, 6 Oct 2025 11:56:15 -0400 Subject: [PATCH 164/190] feat: debug logs (#1460) Co-authored-by: Owl Bot --- .../subscriber/_protocol/requests.py | 2 + .../_protocol/streaming_pull_manager.py | 119 ++++++++++++++---- google/cloud/pubsub_v1/subscriber/message.py | 30 +++++ pytest.ini | 2 + .../unit/pubsub_v1/subscriber/test_message.py | 24 +++- .../subscriber/test_streaming_pull_manager.py | 30 +++-- 6 files changed, 170 insertions(+), 37 deletions(-) diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/requests.py b/google/cloud/pubsub_v1/subscriber/_protocol/requests.py index 6fd35896b..9a0ba5a50 100644 --- a/google/cloud/pubsub_v1/subscriber/_protocol/requests.py +++ b/google/cloud/pubsub_v1/subscriber/_protocol/requests.py @@ -32,6 +32,7 @@ class AckRequest(NamedTuple): ordering_key: Optional[str] future: Optional["futures.Future"] opentelemetry_data: Optional[SubscribeOpenTelemetry] = None + message_id: Optional[str] = None class DropRequest(NamedTuple): @@ -52,6 +53,7 @@ class ModAckRequest(NamedTuple): seconds: float future: Optional["futures.Future"] opentelemetry_data: Optional[SubscribeOpenTelemetry] = None + message_id: Optional[str] = None class NackRequest(NamedTuple): diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py index de3ac3780..d509d8074 100644 --- a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py +++ b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py @@ -21,7 +21,16 @@ import logging import threading import typing -from typing import Any, Dict, Callable, Iterable, List, Optional, Set, Tuple +from typing import ( + Any, + Dict, + Callable, + Iterable, + List, + Optional, + Set, + Tuple, +) import uuid from opentelemetry import trace @@ -60,6 +69,12 @@ _LOGGER = logging.getLogger(__name__) +_SLOW_ACK_LOGGER = logging.getLogger("slow-ack") +_STREAMS_LOGGER = logging.getLogger("subscriber-streams") +_FLOW_CONTROL_LOGGER = logging.getLogger("subscriber-flow-control") +_CALLBACK_DELIVERY_LOGGER = logging.getLogger("callback-delivery") +_CALLBACK_EXCEPTION_LOGGER = logging.getLogger("callback-exceptions") +_EXPIRY_LOGGER = logging.getLogger("expiry") _REGULAR_SHUTDOWN_THREAD_NAME = "Thread-RegularStreamShutdown" _RPC_ERROR_THREAD_NAME = "Thread-OnRpcTerminated" _RETRYABLE_STREAM_ERRORS = ( @@ -145,6 +160,14 @@ def _wrap_callback_errors( callback: The user callback. message: The Pub/Sub message. """ + _CALLBACK_DELIVERY_LOGGER.debug( + "Message (id=%s, ack_id=%s, ordering_key=%s, exactly_once=%s) received by subscriber callback", + message.message_id, + message.ack_id, + message.ordering_key, + message.exactly_once_enabled, + ) + try: if message.opentelemetry_data: message.opentelemetry_data.end_subscribe_concurrency_control_span() @@ -156,9 +179,15 @@ def _wrap_callback_errors( # Note: the likelihood of this failing is extremely low. This just adds # a message to a queue, so if this doesn't work the world is in an # unrecoverable state and this thread should just bail. - _LOGGER.exception( - "Top-level exception occurred in callback while processing a message" + + _CALLBACK_EXCEPTION_LOGGER.exception( + "Message (id=%s, ack_id=%s, ordering_key=%s, exactly_once=%s)'s callback threw exception, nacking message.", + message.message_id, + message.ack_id, + message.ordering_key, + message.exactly_once_enabled, ) + message.nack() on_callback_error(exc) @@ -199,6 +228,9 @@ def _process_requests( error_status: Optional["status_pb2.Status"], ack_reqs_dict: Dict[str, requests.AckRequest], errors_dict: Optional[Dict[str, str]], + ack_histogram: Optional[histogram.Histogram] = None, + # TODO - Change this param to a Union of Literals when we drop p3.7 support + req_type: str = "ack", ): """Process requests when exactly-once delivery is enabled by referring to error_status and errors_dict. @@ -209,28 +241,40 @@ def _process_requests( """ requests_completed = [] requests_to_retry = [] - for ack_id in ack_reqs_dict: + for ack_id, ack_request in ack_reqs_dict.items(): + # Debug logging: slow acks + if ( + req_type == "ack" + and ack_histogram + and ack_request.time_to_ack > ack_histogram.percentile(percent=99) + ): + _SLOW_ACK_LOGGER.debug( + "Message (id=%s, ack_id=%s) ack duration of %s s is higher than the p99 ack duration", + ack_request.message_id, + ack_request.ack_id, + ) + # Handle special errors returned for ack/modack RPCs via the ErrorInfo # sidecar metadata when exactly-once delivery is enabled. if errors_dict and ack_id in errors_dict: exactly_once_error = errors_dict[ack_id] if exactly_once_error.startswith("TRANSIENT_"): - requests_to_retry.append(ack_reqs_dict[ack_id]) + requests_to_retry.append(ack_request) else: if exactly_once_error == "PERMANENT_FAILURE_INVALID_ACK_ID": exc = AcknowledgeError(AcknowledgeStatus.INVALID_ACK_ID, info=None) else: exc = AcknowledgeError(AcknowledgeStatus.OTHER, exactly_once_error) - future = ack_reqs_dict[ack_id].future + future = ack_request.future if future is not None: future.set_exception(exc) - requests_completed.append(ack_reqs_dict[ack_id]) + requests_completed.append(ack_request) # Temporary GRPC errors are retried elif ( error_status and error_status.code in _EXACTLY_ONCE_DELIVERY_TEMPORARY_RETRY_ERRORS ): - requests_to_retry.append(ack_reqs_dict[ack_id]) + requests_to_retry.append(ack_request) # Other GRPC errors are NOT retried elif error_status: if error_status.code == code_pb2.PERMISSION_DENIED: @@ -239,20 +283,20 @@ def _process_requests( exc = AcknowledgeError(AcknowledgeStatus.FAILED_PRECONDITION, info=None) else: exc = AcknowledgeError(AcknowledgeStatus.OTHER, str(error_status)) - future = ack_reqs_dict[ack_id].future + future = ack_request.future if future is not None: future.set_exception(exc) - requests_completed.append(ack_reqs_dict[ack_id]) + requests_completed.append(ack_request) # Since no error occurred, requests with futures are completed successfully. - elif ack_reqs_dict[ack_id].future: - future = ack_reqs_dict[ack_id].future + elif ack_request.future: + future = ack_request.future # success assert future is not None future.set_result(AcknowledgeStatus.SUCCESS) - requests_completed.append(ack_reqs_dict[ack_id]) + requests_completed.append(ack_request) # All other requests are considered completed. else: - requests_completed.append(ack_reqs_dict[ack_id]) + requests_completed.append(ack_request) return requests_completed, requests_to_retry @@ -560,8 +604,10 @@ def maybe_pause_consumer(self) -> None: with self._pause_resume_lock: if self.load >= _MAX_LOAD: if self._consumer is not None and not self._consumer.is_paused: - _LOGGER.debug( - "Message backlog over load at %.2f, pausing.", self.load + _FLOW_CONTROL_LOGGER.debug( + "Message backlog over load at %.2f (threshold %.2f), initiating client-side flow control", + self.load, + _RESUME_THRESHOLD, ) self._consumer.pause() @@ -588,10 +634,18 @@ def maybe_resume_consumer(self) -> None: self._maybe_release_messages() if self.load < _RESUME_THRESHOLD: - _LOGGER.debug("Current load is %.2f, resuming consumer.", self.load) + _FLOW_CONTROL_LOGGER.debug( + "Current load is %.2f (threshold %.2f), suspending client-side flow control.", + self.load, + _RESUME_THRESHOLD, + ) self._consumer.resume() else: - _LOGGER.debug("Did not resume, current load is %.2f.", self.load) + _FLOW_CONTROL_LOGGER.debug( + "Current load is %.2f (threshold %.2f), retaining client-side flow control.", + self.load, + _RESUME_THRESHOLD, + ) def _maybe_release_messages(self) -> None: """Release (some of) the held messages if the current load allows for it. @@ -702,7 +756,7 @@ def send_unary_ack( if self._exactly_once_delivery_enabled(): requests_completed, requests_to_retry = _process_requests( - error_status, ack_reqs_dict, ack_errors_dict + error_status, ack_reqs_dict, ack_errors_dict, self.ack_histogram, "ack" ) else: requests_completed = [] @@ -796,7 +850,11 @@ def send_unary_modack( if self._exactly_once_delivery_enabled(): requests_completed, requests_to_retry = _process_requests( - error_status, ack_reqs_dict, modack_errors_dict + error_status, + ack_reqs_dict, + modack_errors_dict, + self.ack_histogram, + "modack", ) else: requests_completed = [] @@ -1239,6 +1297,11 @@ def _on_response(self, response: gapic_types.StreamingPullResponse) -> None: receipt_modack=True, ) + if len(expired_ack_ids): + _EXPIRY_LOGGER.debug( + "ack ids %s were dropped as they have already expired.", expired_ack_ids + ) + with self._pause_resume_lock: if self._scheduler is None or self._leaser is None: _LOGGER.debug( @@ -1304,9 +1367,13 @@ def _should_recover(self, exception: BaseException) -> bool: # If this is in the list of idempotent exceptions, then we want to # recover. if isinstance(exception, _RETRYABLE_STREAM_ERRORS): - _LOGGER.debug("Observed recoverable stream error %s", exception) + _STREAMS_LOGGER.debug( + "Observed recoverable stream error %s, reopening stream", exception + ) return True - _LOGGER.debug("Observed non-recoverable stream error %s", exception) + _STREAMS_LOGGER.debug( + "Observed non-recoverable stream error %s, shutting down stream", exception + ) return False def _should_terminate(self, exception: BaseException) -> bool: @@ -1326,9 +1393,13 @@ def _should_terminate(self, exception: BaseException) -> bool: is_api_error = isinstance(exception, exceptions.GoogleAPICallError) # Terminate any non-API errors, or non-retryable errors (permission denied, unauthorized, etc.) if not is_api_error or isinstance(exception, _TERMINATING_STREAM_ERRORS): - _LOGGER.debug("Observed terminating stream error %s", exception) + _STREAMS_LOGGER.debug( + "Observed terminating stream error %s, shutting down stream", exception + ) return True - _LOGGER.debug("Observed non-terminating stream error %s", exception) + _STREAMS_LOGGER.debug( + "Observed non-terminating stream error %s, attempting to reopen", exception + ) return False def _on_rpc_done(self, future: Any) -> None: diff --git a/google/cloud/pubsub_v1/subscriber/message.py b/google/cloud/pubsub_v1/subscriber/message.py index 61f60c4d9..aa715ac67 100644 --- a/google/cloud/pubsub_v1/subscriber/message.py +++ b/google/cloud/pubsub_v1/subscriber/message.py @@ -16,6 +16,7 @@ import datetime as dt import json +import logging import math import time import typing @@ -43,6 +44,8 @@ attributes: {} }}""" +_ACK_NACK_LOGGER = logging.getLogger("ack-nack") + _SUCCESS_FUTURE = futures.Future() _SUCCESS_FUTURE.set_result(AcknowledgeStatus.SUCCESS) @@ -274,6 +277,7 @@ def ack(self) -> None: time_to_ack = math.ceil(time.time() - self._received_timestamp) self._request_queue.put( requests.AckRequest( + message_id=self.message_id, ack_id=self._ack_id, byte_size=self.size, time_to_ack=time_to_ack, @@ -282,6 +286,12 @@ def ack(self) -> None: opentelemetry_data=self.opentelemetry_data, ) ) + _ACK_NACK_LOGGER.debug( + "Called ack for message (id=%s, ack_id=%s, ordering_key=%s)", + self.message_id, + self.ack_id, + self.ordering_key, + ) def ack_with_response(self) -> "futures.Future": """Acknowledge the given message. @@ -322,6 +332,12 @@ def ack_with_response(self) -> "futures.Future": pubsub_v1.subscriber.exceptions.AcknowledgeError exception will be thrown. """ + _ACK_NACK_LOGGER.debug( + "Called ack for message (id=%s, ack_id=%s, ordering_key=%s, exactly_once=True)", + self.message_id, + self.ack_id, + self.ordering_key, + ) if self.opentelemetry_data: self.opentelemetry_data.add_process_span_event("ack called") self.opentelemetry_data.end_process_span() @@ -335,6 +351,7 @@ def ack_with_response(self) -> "futures.Future": time_to_ack = math.ceil(time.time() - self._received_timestamp) self._request_queue.put( requests.AckRequest( + message_id=self.message_id, ack_id=self._ack_id, byte_size=self.size, time_to_ack=time_to_ack, @@ -382,6 +399,7 @@ def modify_ack_deadline(self, seconds: int) -> None: """ self._request_queue.put( requests.ModAckRequest( + message_id=self.message_id, ack_id=self._ack_id, seconds=seconds, future=None, @@ -445,6 +463,7 @@ def modify_ack_deadline_with_response(self, seconds: int) -> "futures.Future": self._request_queue.put( requests.ModAckRequest( + message_id=self.message_id, ack_id=self._ack_id, seconds=seconds, future=req_future, @@ -461,6 +480,13 @@ def nack(self) -> None: may take place immediately or after a delay, and may arrive at this subscriber or another. """ + _ACK_NACK_LOGGER.debug( + "Called nack for message (id=%s, ack_id=%s, ordering_key=%s, exactly_once=%s)", + self.message_id, + self.ack_id, + self.ordering_key, + self._exactly_once_delivery_enabled_func(), + ) if self.opentelemetry_data: self.opentelemetry_data.add_process_span_event("nack called") self.opentelemetry_data.end_process_span() @@ -530,3 +556,7 @@ def nack_with_response(self) -> "futures.Future": ) return future + + @property + def exactly_once_enabled(self): + return self._exactly_once_delivery_enabled_func() diff --git a/pytest.ini b/pytest.ini index 09a522efe..fc17230ef 100644 --- a/pytest.ini +++ b/pytest.ini @@ -21,4 +21,6 @@ filterwarnings = # Remove once https://github.com/googleapis/gapic-generator-python/issues/2303 is fixed ignore:The python-bigquery library will stop supporting Python 3.7:PendingDeprecationWarning # Remove once we move off credential files https://github.com/googleapis/google-auth-library-python/pull/1812 + # Note that these are used in tests only ignore:Your config file at [/home/kbuilder/.docker/config.json] contains these credential helper entries:DeprecationWarning + ignore:The `credentials_file` argument is deprecated because of a potential security risk:DeprecationWarning \ No newline at end of file diff --git a/tests/unit/pubsub_v1/subscriber/test_message.py b/tests/unit/pubsub_v1/subscriber/test_message.py index 8d9d2566e..03bdc1514 100644 --- a/tests/unit/pubsub_v1/subscriber/test_message.py +++ b/tests/unit/pubsub_v1/subscriber/test_message.py @@ -289,6 +289,7 @@ def test_ack(): msg.ack() put.assert_called_once_with( requests.AckRequest( + message_id=msg.message_id, ack_id="bogus_ack_id", byte_size=30, time_to_ack=mock.ANY, @@ -305,6 +306,7 @@ def test_ack_with_response_exactly_once_delivery_disabled(): future = msg.ack_with_response() put.assert_called_once_with( requests.AckRequest( + message_id=msg.message_id, ack_id="bogus_ack_id", byte_size=30, time_to_ack=mock.ANY, @@ -325,6 +327,7 @@ def test_ack_with_response_exactly_once_delivery_enabled(): future = msg.ack_with_response() put.assert_called_once_with( requests.AckRequest( + message_id=msg.message_id, ack_id="bogus_ack_id", byte_size=30, time_to_ack=mock.ANY, @@ -350,7 +353,12 @@ def test_modify_ack_deadline(): with mock.patch.object(msg._request_queue, "put") as put: msg.modify_ack_deadline(60) put.assert_called_once_with( - requests.ModAckRequest(ack_id="bogus_ack_id", seconds=60, future=None) + requests.ModAckRequest( + message_id=msg.message_id, + ack_id="bogus_ack_id", + seconds=60, + future=None, + ) ) check_call_types(put, requests.ModAckRequest) @@ -360,7 +368,12 @@ def test_modify_ack_deadline_with_response_exactly_once_delivery_disabled(): with mock.patch.object(msg._request_queue, "put") as put: future = msg.modify_ack_deadline_with_response(60) put.assert_called_once_with( - requests.ModAckRequest(ack_id="bogus_ack_id", seconds=60, future=None) + requests.ModAckRequest( + message_id=msg.message_id, + ack_id="bogus_ack_id", + seconds=60, + future=None, + ) ) assert future.result() == AcknowledgeStatus.SUCCESS assert future == message._SUCCESS_FUTURE @@ -374,7 +387,12 @@ def test_modify_ack_deadline_with_response_exactly_once_delivery_enabled(): with mock.patch.object(msg._request_queue, "put") as put: future = msg.modify_ack_deadline_with_response(60) put.assert_called_once_with( - requests.ModAckRequest(ack_id="bogus_ack_id", seconds=60, future=future) + requests.ModAckRequest( + message_id=msg.message_id, + ack_id="bogus_ack_id", + seconds=60, + future=future, + ) ) check_call_types(put, requests.ModAckRequest) diff --git a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py index f45959637..b9561d747 100644 --- a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py +++ b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import functools import logging import sys import threading @@ -61,6 +60,15 @@ from google.rpc import error_details_pb2 +def create_mock_message(**kwargs): + _message_mock = mock.create_autospec(message.Message, instance=True) + msg = _message_mock.return_value + for k, v in kwargs.items(): + setattr(msg, k, v) + + return msg + + @pytest.mark.parametrize( "exception,expected_cls", [ @@ -80,7 +88,7 @@ def test__wrap_as_exception(exception, expected_cls): def test__wrap_callback_errors_no_error(): - msg = mock.create_autospec(message.Message, instance=True) + msg = create_mock_message() callback = mock.Mock() on_callback_error = mock.Mock() @@ -99,7 +107,7 @@ def test__wrap_callback_errors_no_error(): ], ) def test__wrap_callback_errors_error(callback_error): - msg = mock.create_autospec(message.Message, instance=True) + msg = create_mock_message() callback = mock.Mock(side_effect=callback_error) on_callback_error = mock.Mock() @@ -511,8 +519,8 @@ def test__maybe_release_messages_on_overload(): manager = make_manager( flow_control=types.FlowControl(max_messages=10, max_bytes=1000) ) + msg = create_mock_message(ack_id="ack", size=11) - msg = mock.create_autospec(message.Message, instance=True, ack_id="ack", size=11) manager._messages_on_hold.put(msg) manager._on_hold_bytes = msg.size @@ -539,9 +547,8 @@ def test_opentelemetry__maybe_release_messages_subscribe_scheduler_span(span_exp # max load is hit. _leaser = manager._leaser = mock.create_autospec(leaser.Leaser) fake_leaser_add(_leaser, init_msg_count=8, assumed_msg_size=10) - msg = mock.create_autospec( - message.Message, instance=True, ack_id="ack_foo", size=10 - ) + msg = create_mock_message(ack_id="ack_foo", size=10) + msg.message_id = 3 opentelemetry_data = SubscribeOpenTelemetry(msg) msg.opentelemetry_data = opentelemetry_data @@ -611,7 +618,7 @@ def test__maybe_release_messages_negative_on_hold_bytes_warning( ) manager._callback = lambda msg: msg # pragma: NO COVER - msg = mock.create_autospec(message.Message, instance=True, ack_id="ack", size=17) + msg = create_mock_message(ack_id="ack", size=17) manager._messages_on_hold.put(msg) manager._on_hold_bytes = 5 # too low for some reason @@ -1633,8 +1640,11 @@ def test_close_nacks_internally_queued_messages(): def fake_nack(self): nacked_messages.append(self.data) - MockMsg = functools.partial(mock.create_autospec, message.Message, instance=True) - messages = [MockMsg(data=b"msg1"), MockMsg(data=b"msg2"), MockMsg(data=b"msg3")] + messages = [ + create_message(data=b"msg1"), + create_message(data=b"msg2"), + create_message(data=b"msg3"), + ] for msg in messages: msg.nack = stdlib_types.MethodType(fake_nack, msg) From 716bb0b7444d69456aefc864eaf58a725a4060bb Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Tue, 7 Oct 2025 11:21:24 -0400 Subject: [PATCH 165/190] chore: update owlbot post processor image (#1502) --- .github/.OwlBot.lock.yaml | 4 ++-- owlbot.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 508ba98ef..9a7846675 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:25de45b58e52021d3a24a6273964371a97a4efeefe6ad3845a64e697c63b6447 -# created: 2025-04-14T14:34:43.260858345Z + digest: sha256:4a9e5d44b98e8672e2037ee22bc6b4f8e844a2d75fcb78ea8a4b38510112abc6 +# created: 2025-10-07 diff --git a/owlbot.py b/owlbot.py index 2a131d557..c58e5a67a 100644 --- a/owlbot.py +++ b/owlbot.py @@ -343,7 +343,7 @@ system_test_python_versions=["3.12"], system_test_external_dependencies=["psutil","flaky"], ) -s.move(templated_files, excludes=[".coveragerc", ".github/blunderbuss.yml", ".github/release-please.yml", "README.rst", "docs/index.rst"]) +s.move(templated_files, excludes=[".coveragerc", ".github/**", "README.rst", "docs/**", ".kokoro/**"]) python.py_samples(skip_readmes=True) From e6294a1883abf9809cb56d5cd4ad25cc501bc994 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 7 Oct 2025 16:41:31 -0400 Subject: [PATCH 166/190] feat: support the protocol version in StreamingPullRequest (#1455) Co-authored-by: Owl Bot --- .../pubsub_v1/services/subscriber/client.py | 2 +- google/pubsub_v1/types/__init__.py | 5 +- google/pubsub_v1/types/pubsub.py | 58 +++++++++++-------- mypy.ini | 4 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- scripts/fixup_pubsub_v1_keywords.py | 2 +- 6 files changed, 39 insertions(+), 34 deletions(-) diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index f5945ba64..d928d3678 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -1897,7 +1897,7 @@ def request_generator(): # Wrappers in api-core should not automatically pre-fetch the first # stream result, as this breaks the stream when re-opening it. # https://github.com/googleapis/python-pubsub/issues/93#issuecomment-630762257 - self._transport.streaming_pull._prefetch_first_result_ = False # type: ignore + self._transport.streaming_pull._prefetch_first_result_ = False # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. diff --git a/google/pubsub_v1/types/__init__.py b/google/pubsub_v1/types/__init__.py index c4f020ee2..85c6b901b 100644 --- a/google/pubsub_v1/types/__init__.py +++ b/google/pubsub_v1/types/__init__.py @@ -87,13 +87,12 @@ Encoding, SchemaView, ) -import google.api_core.timeout TimeoutType = Union[ int, float, - google.api_core.timeout.ConstantTimeout, - google.api_core.timeout.ExponentialTimeout, + "google.api_core.timeout.ConstantTimeout", + "google.api_core.timeout.ExponentialTimeout", ] """The type of the timeout parameter of publisher client methods.""" diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index 227da2208..9fc8f87eb 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -241,13 +241,13 @@ class State(proto.Enum): Permission denied encountered while consuming data from Kinesis. This can happen if: - - The provided ``aws_role_arn`` does not exist or does not - have the appropriate permissions attached. - - The provided ``aws_role_arn`` is not set up properly for - Identity Federation using ``gcp_service_account``. - - The Pub/Sub SA is not granted the - ``iam.serviceAccounts.getOpenIdToken`` permission on - ``gcp_service_account``. + - The provided ``aws_role_arn`` does not exist or does not + have the appropriate permissions attached. + - The provided ``aws_role_arn`` is not set up properly for + Identity Federation using ``gcp_service_account``. + - The Pub/Sub SA is not granted the + ``iam.serviceAccounts.getOpenIdToken`` permission on + ``gcp_service_account``. PUBLISH_PERMISSION_DENIED (3): Permission denied encountered while publishing to the topic. This can happen if the Pub/Sub SA has not been granted the @@ -347,9 +347,9 @@ class State(proto.Enum): granted the `appropriate permissions `__: - - storage.objects.list: to list the objects in a bucket. - - storage.objects.get: to read the objects in a bucket. - - storage.buckets.get: to verify the bucket exists. + - storage.objects.list: to list the objects in a bucket. + - storage.objects.get: to read the objects in a bucket. + - storage.buckets.get: to verify the bucket exists. PUBLISH_PERMISSION_DENIED (3): Permission denied encountered while publishing to the topic. This can happen if the Pub/Sub SA has not been granted the @@ -1991,11 +1991,11 @@ class Subscription(proto.Message): for the delivery of a message with a given value of ``message_id`` on this subscription: - - The message sent to a subscriber is guaranteed not to be - resent before the message's acknowledgment deadline - expires. - - An acknowledged message will not be resent to a - subscriber. + - The message sent to a subscriber is guaranteed not to be + resent before the message's acknowledgment deadline + expires. + - An acknowledged message will not be resent to a + subscriber. Note that subscribers may still receive multiple copies of a message when ``enable_exactly_once_delivery`` is true if the @@ -2309,10 +2309,10 @@ class PushConfig(proto.Message): The only supported values for the ``x-goog-version`` attribute are: - - ``v1beta1``: uses the push format defined in the v1beta1 - Pub/Sub API. - - ``v1`` or ``v1beta2``: uses the push format defined in - the v1 Pub/Sub API. + - ``v1beta1``: uses the push format defined in the v1beta1 + Pub/Sub API. + - ``v1`` or ``v1beta2``: uses the push format defined in the + v1 Pub/Sub API. For example: ``attributes { "x-goog-version": "v1" }`` oidc_token (google.pubsub_v1.types.PushConfig.OidcToken): @@ -2478,12 +2478,11 @@ class State(proto.Enum): Cannot write to the BigQuery table because of permission denied errors. This can happen if - - Pub/Sub SA has not been granted the `appropriate BigQuery - IAM - permissions `__ - - bigquery.googleapis.com API is not enabled for the - project - (`instructions `__) + - Pub/Sub SA has not been granted the `appropriate BigQuery + IAM + permissions `__ + - bigquery.googleapis.com API is not enabled for the project + (`instructions `__) NOT_FOUND (3): Cannot write to the BigQuery table because it does not exist. @@ -3111,6 +3110,11 @@ class StreamingPullRequest(proto.Message): only be set on the initial StreamingPullRequest. If it is set on a subsequent request, the stream will be aborted with status ``INVALID_ARGUMENT``. + protocol_version (int): + Optional. The protocol version used by the client. This + property can only be set on the initial + StreamingPullRequest. If it is set on a subsequent request, + the stream will be aborted with status ``INVALID_ARGUMENT``. """ subscription: str = proto.Field( @@ -3145,6 +3149,10 @@ class StreamingPullRequest(proto.Message): proto.INT64, number=8, ) + protocol_version: int = proto.Field( + proto.INT64, + number=10, + ) class StreamingPullResponse(proto.Message): diff --git a/mypy.ini b/mypy.ini index 00f693e26..574c5aed3 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,5 +1,3 @@ [mypy] -python_version = 3.8 +python_version = 3.7 namespace_packages = True -# Autogenerated folder - TODO remove this https://github.com/googleapis/python-pubsub/issues/536 -exclude = google/pubsub_v1 diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 1e6003150..dd0d6423a 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.31.1" + "version": "0.1.0" }, "snippets": [ { diff --git a/scripts/fixup_pubsub_v1_keywords.py b/scripts/fixup_pubsub_v1_keywords.py index 3bb90d780..9f5619e8c 100644 --- a/scripts/fixup_pubsub_v1_keywords.py +++ b/scripts/fixup_pubsub_v1_keywords.py @@ -68,7 +68,7 @@ class pubsubCallTransformer(cst.CSTTransformer): 'pull': ('subscription', 'max_messages', 'return_immediately', ), 'rollback_schema': ('name', 'revision_id', ), 'seek': ('subscription', 'time', 'snapshot', ), - 'streaming_pull': ('subscription', 'stream_ack_deadline_seconds', 'ack_ids', 'modify_deadline_seconds', 'modify_deadline_ack_ids', 'client_id', 'max_outstanding_messages', 'max_outstanding_bytes', ), + 'streaming_pull': ('subscription', 'stream_ack_deadline_seconds', 'ack_ids', 'modify_deadline_seconds', 'modify_deadline_ack_ids', 'client_id', 'max_outstanding_messages', 'max_outstanding_bytes', 'protocol_version', ), 'update_snapshot': ('snapshot', 'update_mask', ), 'update_subscription': ('subscription', 'update_mask', ), 'update_topic': ('topic', 'update_mask', ), From 95a26907efecfa5d56b140b7f833640b7fbb21d7 Mon Sep 17 00:00:00 2001 From: Chalmer Lowe Date: Wed, 22 Oct 2025 16:18:27 -0400 Subject: [PATCH 167/190] feat: Adds Python 3.14 support (#1512) Co-authored-by: Owl Bot --- .github/sync-repo-settings.yaml | 2 + .github/workflows/unittest.yml | 2 +- .kokoro/presubmit/presubmit.cfg | 7 ++- .kokoro/samples/python3.14/common.cfg | 40 +++++++++++++++++ .kokoro/samples/python3.14/continuous.cfg | 6 +++ .kokoro/samples/python3.14/periodic-head.cfg | 11 +++++ .kokoro/samples/python3.14/periodic.cfg | 6 +++ .kokoro/samples/python3.14/presubmit.cfg | 6 +++ CONTRIBUTING.rst | 4 +- .../_protocol/streaming_pull_manager.py | 2 +- .../cloud/pubsub_v1/subscriber/scheduler.py | 23 +++++++++- noxfile.py | 23 +++++++--- owlbot.py | 2 +- setup.py | 4 +- testing/constraints-3.14.txt | 13 ++++++ .../publisher/test_publisher_client.py | 45 ++++++++++--------- 16 files changed, 162 insertions(+), 34 deletions(-) create mode 100644 .kokoro/samples/python3.14/common.cfg create mode 100644 .kokoro/samples/python3.14/continuous.cfg create mode 100644 .kokoro/samples/python3.14/periodic-head.cfg create mode 100644 .kokoro/samples/python3.14/periodic.cfg create mode 100644 .kokoro/samples/python3.14/presubmit.cfg create mode 100644 testing/constraints-3.14.txt diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml index 77c1a4fb5..bfde18cc0 100644 --- a/.github/sync-repo-settings.yaml +++ b/.github/sync-repo-settings.yaml @@ -27,4 +27,6 @@ branchProtectionRules: - 'unit (3.10)' - 'unit (3.11)' - 'unit (3.12)' + - 'unit (3.13)' + - 'unit (3.14)' - 'cover' diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index 6a0429d96..d59bbb1b8 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] + python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.kokoro/presubmit/presubmit.cfg b/.kokoro/presubmit/presubmit.cfg index 8f43917d9..227ccdf47 100644 --- a/.kokoro/presubmit/presubmit.cfg +++ b/.kokoro/presubmit/presubmit.cfg @@ -1 +1,6 @@ -# Format: //devtools/kokoro/config/proto/build.proto \ No newline at end of file +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "NOX_SESSION" + value: "system-3.12 blacken mypy format" +} diff --git a/.kokoro/samples/python3.14/common.cfg b/.kokoro/samples/python3.14/common.cfg new file mode 100644 index 000000000..f6feff705 --- /dev/null +++ b/.kokoro/samples/python3.14/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.14" +} + +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-314" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-pubsub/.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-pubsub/.kokoro/trampoline_v2.sh" diff --git a/.kokoro/samples/python3.14/continuous.cfg b/.kokoro/samples/python3.14/continuous.cfg new file mode 100644 index 000000000..b19681787 --- /dev/null +++ b/.kokoro/samples/python3.14/continuous.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} diff --git a/.kokoro/samples/python3.14/periodic-head.cfg b/.kokoro/samples/python3.14/periodic-head.cfg new file mode 100644 index 000000000..f9cfcd33e --- /dev/null +++ b/.kokoro/samples/python3.14/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-pubsub/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/samples/python3.14/periodic.cfg b/.kokoro/samples/python3.14/periodic.cfg new file mode 100644 index 000000000..71cd1e597 --- /dev/null +++ b/.kokoro/samples/python3.14/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.14/presubmit.cfg b/.kokoro/samples/python3.14/presubmit.cfg new file mode 100644 index 000000000..b19681787 --- /dev/null +++ b/.kokoro/samples/python3.14/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index f153c3ae7..417b1e9f8 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -22,7 +22,7 @@ In order to add a feature: documentation. - The feature must work fully on the following CPython versions: - 3.7, 3.8, 3.9, 3.10, 3.11, 3.12 and 3.13 on both UNIX and Windows. + 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 and 3.14 on both UNIX and Windows. - The feature must not add unnecessary dependencies (where "unnecessary" is of course subjective, but new dependencies should @@ -228,6 +228,7 @@ We support: - `Python 3.11`_ - `Python 3.12`_ - `Python 3.13`_ +- `Python 3.14`_ .. _Python 3.7: https://docs.python.org/3.7/ .. _Python 3.8: https://docs.python.org/3.8/ @@ -236,6 +237,7 @@ We support: .. _Python 3.11: https://docs.python.org/3.11/ .. _Python 3.12: https://docs.python.org/3.12/ .. _Python 3.13: https://docs.python.org/3.13/ +.. _Python 3.14: https://docs.python.org/3.14/ Supported versions can be found in our ``noxfile.py`` `config`_. diff --git a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py index d509d8074..5132456a2 100644 --- a/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py +++ b/google/cloud/pubsub_v1/subscriber/_protocol/streaming_pull_manager.py @@ -458,7 +458,7 @@ def dispatcher(self) -> Optional[dispatcher.Dispatcher]: return self._dispatcher @property - def leaser(self) -> Optional[leaser.Leaser]: + def leaser(self) -> Optional["leaser.Leaser"]: """The leaser helper.""" return self._leaser diff --git a/google/cloud/pubsub_v1/subscriber/scheduler.py b/google/cloud/pubsub_v1/subscriber/scheduler.py index a3b3c88e1..cc3393bd7 100644 --- a/google/cloud/pubsub_v1/subscriber/scheduler.py +++ b/google/cloud/pubsub_v1/subscriber/scheduler.py @@ -21,6 +21,7 @@ import abc import concurrent.futures import queue +import sys import typing from typing import Callable, List, Optional import warnings @@ -37,7 +38,7 @@ class Scheduler(metaclass=abc.ABCMeta): @property @abc.abstractmethod - def queue(self) -> queue.Queue: # pragma: NO COVER + def queue(self) -> "queue.Queue": # pragma: NO COVER """Queue: A concurrency-safe queue specific to the underlying concurrency implementation. @@ -162,7 +163,25 @@ def shutdown( work_item = self._executor._work_queue.get(block=False) if work_item is None: # Exceutor in shutdown mode. continue - dropped_messages.append(work_item.args[0]) # type: ignore[index] + + dropped_message = None + if sys.version_info < (3, 14): + # For Python < 3.14, work_item.args is a tuple of positional arguments. + # The message is expected to be the first argument. + if hasattr(work_item, "args") and work_item.args: + dropped_message = work_item.args[0] # type: ignore[index] + else: + # For Python >= 3.14, work_item.task is (fn, args, kwargs). + # The message is expected to be the first item in the args tuple (task[1]). + if ( + hasattr(work_item, "task") + and len(work_item.task) == 3 + and work_item.task[1] + ): + dropped_message = work_item.task[1][0] + + if dropped_message is not None: + dropped_messages.append(dropped_message) except queue.Empty: pass diff --git a/noxfile.py b/noxfile.py index 70e65a571..7455daf83 100644 --- a/noxfile.py +++ b/noxfile.py @@ -44,6 +44,7 @@ "3.11", "3.12", "3.13", + "3.14", ] UNIT_TEST_STANDARD_DEPENDENCIES = [ "mock", @@ -234,7 +235,12 @@ def install_unittest_dependencies(session, *constraints): def unit(session, protobuf_implementation): # Install all test dependencies, then install this package in-place. - if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12", "3.13"): + if protobuf_implementation == "cpp" and session.python in ( + "3.11", + "3.12", + "3.13", + "3.14", + ): session.skip("cpp implementation is not supported in python 3.11+") constraints_path = str( @@ -325,15 +331,15 @@ def system(session): if system_test_exists: session.run( "py.test", - "--quiet", + "--verbose", f"--junitxml=system_{session.python}_sponge_log.xml", system_test_path, *session.posargs, ) - if system_test_folder_exists: + if os.path.exists(system_test_folder_path): session.run( "py.test", - "--quiet", + "--verbose", f"--junitxml=system_{session.python}_sponge_log.xml", system_test_folder_path, *session.posargs, @@ -436,7 +442,7 @@ def docfx(session): ) -@nox.session(python="3.13") +@nox.session(python="3.14") @nox.parametrize( "protobuf_implementation", ["python", "upb", "cpp"], @@ -444,7 +450,12 @@ def docfx(session): def prerelease_deps(session, protobuf_implementation): """Run all tests with prerelease versions of dependencies installed.""" - if protobuf_implementation == "cpp" and session.python in ("3.11", "3.12", "3.13"): + if protobuf_implementation == "cpp" and session.python in ( + "3.11", + "3.12", + "3.13", + "3.14", + ): session.skip("cpp implementation is not supported in python 3.11+") # Install all dependencies diff --git a/owlbot.py b/owlbot.py index c58e5a67a..abaf534e2 100644 --- a/owlbot.py +++ b/owlbot.py @@ -338,7 +338,7 @@ samples=True, cov_level=99, versions=gcp.common.detect_versions(path="./google", default_first=True), - unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"], + unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"], unit_test_dependencies=["flaky"], system_test_python_versions=["3.12"], system_test_external_dependencies=["psutil","flaky"], diff --git a/setup.py b/setup.py index 899cefde6..6dbea105a 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,8 @@ release_status = "Development Status :: 5 - Production/Stable" dependencies = [ - "grpcio >= 1.51.3, < 2.0.0", # https://github.com/googleapis/python-pubsub/issues/609 + "grpcio >= 1.51.3, < 2.0.0; python_version < '3.14'", # https://github.com/googleapis/python-pubsub/issues/609 + "grpcio >= 1.75.1, < 2.0.0; python_version >= '3.14'", # google-api-core >= 1.34.0 is allowed in order to support google-api-core 1.x "google-auth >= 2.14.1, <3.0.0", "google-api-core[grpc] >= 1.34.0, <3.0.0,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,!=2.10.*", @@ -88,6 +89,7 @@ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Operating System :: OS Independent", "Topic :: Internet", ], diff --git a/testing/constraints-3.14.txt b/testing/constraints-3.14.txt new file mode 100644 index 000000000..1dba0484d --- /dev/null +++ b/testing/constraints-3.14.txt @@ -0,0 +1,13 @@ +# We use the constraints file for the latest Python version +# (currently this file) to check that the latest +# major versions of dependencies are supported in setup.py. +# List all library dependencies and extras in this file. +# Require the latest major version be installed for each dependency. +# e.g., if setup.py has "google-cloud-foo >= 1.14.0, < 2.0.0", +# Then this file should have google-cloud-foo>=1 +google-api-core>=2 +google-auth>=2 +proto-plus>=1 +protobuf>=6 +grpc-google-iam-v1>=0 +grpcio >= 1.75.1 diff --git a/tests/unit/pubsub_v1/publisher/test_publisher_client.py b/tests/unit/pubsub_v1/publisher/test_publisher_client.py index d1b7d4a81..651c040ba 100644 --- a/tests/unit/pubsub_v1/publisher/test_publisher_client.py +++ b/tests/unit/pubsub_v1/publisher/test_publisher_client.py @@ -308,33 +308,38 @@ def test_opentelemetry_flow_control_exception(creds, span_exporter): future2.result() spans = span_exporter.get_finished_spans() - # Span 1 = Publisher Flow Control Span of first publish - # Span 2 = Publisher Batching Span of first publish - # Span 3 = Publisher Flow Control Span of second publish(raises FlowControlLimitError) - # Span 4 = Publish Create Span of second publish(raises FlowControlLimitError) - assert len(spans) == 4 - failed_flow_control_span = spans[2] - finished_publish_create_span = spans[3] + # Find the spans related to the second, failing publish call + failed_create_span = None + failed_fc_span = None + for span in spans: + if span.name == "topicID create": + if span.status.status_code == trace.StatusCode.ERROR: + failed_create_span = span + elif span.name == "publisher flow control": + if span.status.status_code == trace.StatusCode.ERROR: + failed_fc_span = span + + assert failed_create_span is not None, "Failed 'topicID create' span not found" + assert failed_fc_span is not None, "Failed 'publisher flow control' span not found" # Verify failed flow control span values. - assert failed_flow_control_span.name == "publisher flow control" - assert failed_flow_control_span.kind == trace.SpanKind.INTERNAL + assert failed_fc_span.kind == trace.SpanKind.INTERNAL assert ( - failed_flow_control_span.parent.span_id - == finished_publish_create_span.get_span_context().span_id + failed_fc_span.parent.span_id == failed_create_span.get_span_context().span_id ) - assert failed_flow_control_span.status.status_code == trace.StatusCode.ERROR - - assert len(failed_flow_control_span.events) == 1 - assert failed_flow_control_span.events[0].name == "exception" + assert len(failed_fc_span.events) == 1 + assert failed_fc_span.events[0].name == "exception" # Verify finished publish create span values - assert finished_publish_create_span.name == "topicID create" - assert finished_publish_create_span.status.status_code == trace.StatusCode.ERROR - assert len(finished_publish_create_span.events) == 2 - assert finished_publish_create_span.events[0].name == "publish start" - assert finished_publish_create_span.events[1].name == "exception" + assert failed_create_span.status.status_code == trace.StatusCode.ERROR + assert len(failed_create_span.events) >= 1 # Should have at least 'publish start' + assert failed_create_span.events[0].name == "publish start" + # Check for exception event + has_exception_event = any( + event.name == "exception" for event in failed_create_span.events + ) + assert has_exception_event, "Exception event not found in failed create span" @pytest.mark.skipif( From 80a7b4cf772c3d673a204b34e26cb0646654b13d Mon Sep 17 00:00:00 2001 From: ohmayr Date: Mon, 27 Oct 2025 08:13:43 -0700 Subject: [PATCH 168/190] chore(librarian): onboard to librarian (#1515) --- .librarian/config.yaml | 6 ++++++ .librarian/state.yaml | 10 ++++++++++ 2 files changed, 16 insertions(+) create mode 100644 .librarian/config.yaml create mode 100644 .librarian/state.yaml diff --git a/.librarian/config.yaml b/.librarian/config.yaml new file mode 100644 index 000000000..111f94dd5 --- /dev/null +++ b/.librarian/config.yaml @@ -0,0 +1,6 @@ +global_files_allowlist: + # Allow the container to read and write the root `CHANGELOG.md` + # file during the `release` step to update the latest client library + # versions which are hardcoded in the file. + - path: "CHANGELOG.md" + permissions: "read-write" diff --git a/.librarian/state.yaml b/.librarian/state.yaml new file mode 100644 index 000000000..deaa22de7 --- /dev/null +++ b/.librarian/state.yaml @@ -0,0 +1,10 @@ +image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator:latest +libraries: + - id: google-cloud-pubsub + version: 2.31.1 + apis: [] + source_roots: + - . + preserve_regex: [] + remove_regex: [] + tag_format: v{version} From 27ca7e8ea0a038d72df17d93887d81765b3d44c8 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Mon, 27 Oct 2025 16:10:53 -0700 Subject: [PATCH 169/190] Revert "chore(librarian): onboard to librarian" (#1544) --- .librarian/config.yaml | 6 ------ .librarian/state.yaml | 10 ---------- 2 files changed, 16 deletions(-) delete mode 100644 .librarian/config.yaml delete mode 100644 .librarian/state.yaml diff --git a/.librarian/config.yaml b/.librarian/config.yaml deleted file mode 100644 index 111f94dd5..000000000 --- a/.librarian/config.yaml +++ /dev/null @@ -1,6 +0,0 @@ -global_files_allowlist: - # Allow the container to read and write the root `CHANGELOG.md` - # file during the `release` step to update the latest client library - # versions which are hardcoded in the file. - - path: "CHANGELOG.md" - permissions: "read-write" diff --git a/.librarian/state.yaml b/.librarian/state.yaml deleted file mode 100644 index deaa22de7..000000000 --- a/.librarian/state.yaml +++ /dev/null @@ -1,10 +0,0 @@ -image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator:latest -libraries: - - id: google-cloud-pubsub - version: 2.31.1 - apis: [] - source_roots: - - . - preserve_regex: [] - remove_regex: [] - tag_format: v{version} From 8e28dea5b68fc940266d0b1a9f2a07a7b5f10b34 Mon Sep 17 00:00:00 2001 From: Andrew Browne <81702808+abbrowne126@users.noreply.github.com> Date: Tue, 28 Oct 2025 13:46:24 -0400 Subject: [PATCH 170/190] fix: ignore future warnings on python versions (#1546) --- pytest.ini | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index fc17230ef..165f566b6 100644 --- a/pytest.ini +++ b/pytest.ini @@ -23,4 +23,7 @@ filterwarnings = # Remove once we move off credential files https://github.com/googleapis/google-auth-library-python/pull/1812 # Note that these are used in tests only ignore:Your config file at [/home/kbuilder/.docker/config.json] contains these credential helper entries:DeprecationWarning - ignore:The `credentials_file` argument is deprecated because of a potential security risk:DeprecationWarning \ No newline at end of file + ignore:The `credentials_file` argument is deprecated because of a potential security risk:DeprecationWarning + ignore:You are using a Python version.*which Google will stop supporting in new releases of google\.api_core.*:FutureWarning + ignore:You are using a non-supported Python version \(([\d\.]+)\)\. Google will not post any further updates to google\.api_core.*:FutureWarning + ignore:You are using a Python version \(([\d\.]+)\) past its end of life\. Google will update google\.api_core.*:FutureWarning From 01ab5ea63eda4905d8b274a3300b8a002ecd4119 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 11:13:09 -0700 Subject: [PATCH 171/190] chore(main): release 2.32.0 (#1499) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 1c48d61d0..7bc1375d5 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.31.1" + ".": "2.32.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index da524d1a0..cbe6cea28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.32.0](https://github.com/googleapis/python-pubsub/compare/v2.31.1...v2.32.0) (2025-10-28) + + +### Features + +* Adds Python 3.14 support ([#1512](https://github.com/googleapis/python-pubsub/issues/1512)) ([95a2690](https://github.com/googleapis/python-pubsub/commit/95a26907efecfa5d56b140b7f833640b7fbb21d7)) +* Debug logs ([#1460](https://github.com/googleapis/python-pubsub/issues/1460)) ([b5d4a45](https://github.com/googleapis/python-pubsub/commit/b5d4a458ca9319bebbe3142a1f05d4d4471c8d4d)) +* Support the protocol version in StreamingPullRequest ([#1455](https://github.com/googleapis/python-pubsub/issues/1455)) ([e6294a1](https://github.com/googleapis/python-pubsub/commit/e6294a1883abf9809cb56d5cd4ad25cc501bc994)) + + +### Bug Fixes + +* Ignore future warnings on python versions ([#1546](https://github.com/googleapis/python-pubsub/issues/1546)) ([8e28dea](https://github.com/googleapis/python-pubsub/commit/8e28dea5b68fc940266d0b1a9f2a07a7b5f10b34)) + ## [2.31.1](https://github.com/googleapis/python-pubsub/compare/v2.31.0...v2.31.1) (2025-07-28) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 79eaa5593..3c958586f 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.31.1" # {x-release-please-version} +__version__ = "2.32.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 79eaa5593..3c958586f 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.31.1" # {x-release-please-version} +__version__ = "2.32.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index dd0d6423a..23d997304 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "0.1.0" + "version": "2.32.0" }, "snippets": [ { From 860ec818c1d3e2eaa99db3a7b4306e628fc6bc0d Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 14:43:31 -0400 Subject: [PATCH 172/190] chore(python): Add Python 3.14 to python post processor image (#1504) Co-authored-by: Owl Bot Co-authored-by: Andrew Browne <81702808+abbrowne126@users.noreply.github.com> --- .github/.OwlBot.lock.yaml | 4 ++-- samples/snippets/noxfile.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index 9a7846675..4a311db02 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:4a9e5d44b98e8672e2037ee22bc6b4f8e844a2d75fcb78ea8a4b38510112abc6 -# created: 2025-10-07 + digest: sha256:543e209e7c1c1ffe720eb4db1a3f045a75099304fb19aa11a47dc717b8aae2a9 +# created: 2025-10-09T14:48:42.914384887Z diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index c9a3d1ecb..c326375be 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/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", "3.12", "3.13"] +ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] From 625c261c3138d023a68aa6f312c352920bdb6774 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Wed, 29 Oct 2025 11:29:43 -0700 Subject: [PATCH 173/190] chore(librarian): onboard to librarian (#1547) --- .librarian/config.yaml | 6 ++++++ .librarian/state.yaml | 10 ++++++++++ 2 files changed, 16 insertions(+) create mode 100644 .librarian/config.yaml create mode 100644 .librarian/state.yaml diff --git a/.librarian/config.yaml b/.librarian/config.yaml new file mode 100644 index 000000000..111f94dd5 --- /dev/null +++ b/.librarian/config.yaml @@ -0,0 +1,6 @@ +global_files_allowlist: + # Allow the container to read and write the root `CHANGELOG.md` + # file during the `release` step to update the latest client library + # versions which are hardcoded in the file. + - path: "CHANGELOG.md" + permissions: "read-write" diff --git a/.librarian/state.yaml b/.librarian/state.yaml new file mode 100644 index 000000000..deaa22de7 --- /dev/null +++ b/.librarian/state.yaml @@ -0,0 +1,10 @@ +image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator:latest +libraries: + - id: google-cloud-pubsub + version: 2.31.1 + apis: [] + source_roots: + - . + preserve_regex: [] + remove_regex: [] + tag_format: v{version} From ac6809350758306f28fa1ab46939bc438b5a5e19 Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 12:40:45 -0700 Subject: [PATCH 174/190] feat: Annotate some resource fields with their corresponding API types (#1503) Co-authored-by: Owl Bot Co-authored-by: Andrew Browne <81702808+abbrowne126@users.noreply.github.com> --- .../services/publisher/async_client.py | 4 + google/pubsub_v1/services/publisher/client.py | 41 ++++++++++ .../services/publisher/transports/base.py | 5 +- .../services/publisher/transports/grpc.py | 8 +- .../publisher/transports/grpc_asyncio.py | 8 +- .../services/publisher/transports/rest.py | 5 +- .../schema_service/transports/base.py | 5 +- .../schema_service/transports/grpc.py | 8 +- .../schema_service/transports/grpc_asyncio.py | 8 +- .../schema_service/transports/rest.py | 5 +- .../services/subscriber/async_client.py | 2 + .../pubsub_v1/services/subscriber/client.py | 24 ++++++ .../services/subscriber/transports/base.py | 5 +- .../services/subscriber/transports/grpc.py | 8 +- .../subscriber/transports/grpc_asyncio.py | 8 +- .../services/subscriber/transports/rest.py | 5 +- google/pubsub_v1/types/pubsub.py | 52 ++++++++++++- scripts/fixup_pubsub_v1_keywords.py | 6 +- testing/constraints-3.10.txt | 2 + testing/constraints-3.11.txt | 2 + testing/constraints-3.12.txt | 2 + testing/constraints-3.13.txt | 1 + testing/constraints-3.9.txt | 2 + tests/unit/gapic/pubsub_v1/test_publisher.py | 60 ++++++++++++++- tests/unit/gapic/pubsub_v1/test_subscriber.py | 77 +++++++++++++------ 25 files changed, 290 insertions(+), 63 deletions(-) diff --git a/google/pubsub_v1/services/publisher/async_client.py b/google/pubsub_v1/services/publisher/async_client.py index 52f1f1e30..3767a460b 100644 --- a/google/pubsub_v1/services/publisher/async_client.py +++ b/google/pubsub_v1/services/publisher/async_client.py @@ -80,8 +80,12 @@ class PublisherAsyncClient: _DEFAULT_ENDPOINT_TEMPLATE = PublisherClient._DEFAULT_ENDPOINT_TEMPLATE _DEFAULT_UNIVERSE = PublisherClient._DEFAULT_UNIVERSE + crypto_key_path = staticmethod(PublisherClient.crypto_key_path) + parse_crypto_key_path = staticmethod(PublisherClient.parse_crypto_key_path) schema_path = staticmethod(PublisherClient.schema_path) parse_schema_path = staticmethod(PublisherClient.parse_schema_path) + snapshot_path = staticmethod(PublisherClient.snapshot_path) + parse_snapshot_path = staticmethod(PublisherClient.parse_snapshot_path) subscription_path = staticmethod(PublisherClient.subscription_path) parse_subscription_path = staticmethod(PublisherClient.parse_subscription_path) topic_path = staticmethod(PublisherClient.topic_path) diff --git a/google/pubsub_v1/services/publisher/client.py b/google/pubsub_v1/services/publisher/client.py index 28cfa680b..6debee7ee 100644 --- a/google/pubsub_v1/services/publisher/client.py +++ b/google/pubsub_v1/services/publisher/client.py @@ -215,6 +215,30 @@ def transport(self) -> PublisherTransport: """ return self._transport + @staticmethod + def crypto_key_path( + project: str, + location: str, + key_ring: str, + crypto_key: str, + ) -> str: + """Returns a fully-qualified crypto_key string.""" + return "projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}".format( + project=project, + location=location, + key_ring=key_ring, + crypto_key=crypto_key, + ) + + @staticmethod + def parse_crypto_key_path(path: str) -> Dict[str, str]: + """Parses a crypto_key path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/keyRings/(?P.+?)/cryptoKeys/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + @staticmethod def schema_path( project: str, @@ -232,6 +256,23 @@ def parse_schema_path(path: str) -> Dict[str, str]: m = re.match(r"^projects/(?P.+?)/schemas/(?P.+?)$", path) return m.groupdict() if m else {} + @staticmethod + def snapshot_path( + project: str, + snapshot: str, + ) -> str: + """Returns a fully-qualified snapshot string.""" + return "projects/{project}/snapshots/{snapshot}".format( + project=project, + snapshot=snapshot, + ) + + @staticmethod + def parse_snapshot_path(path: str) -> Dict[str, str]: + """Parses a snapshot path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/snapshots/(?P.+?)$", path) + return m.groupdict() if m else {} + @staticmethod def subscription_path( project: str, diff --git a/google/pubsub_v1/services/publisher/transports/base.py b/google/pubsub_v1/services/publisher/transports/base.py index 0fb41c922..b9d6a6279 100644 --- a/google/pubsub_v1/services/publisher/transports/base.py +++ b/google/pubsub_v1/services/publisher/transports/base.py @@ -73,9 +73,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can + credentials_file (Optional[str]): Deprecated. A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is mutually exclusive with credentials. + This argument is mutually exclusive with credentials. This argument will be + removed in the next major version of this library. scopes (Optional[Sequence[str]]): A list of scopes. quota_project_id (Optional[str]): An optional project to use for billing and quota. diff --git a/google/pubsub_v1/services/publisher/transports/grpc.py b/google/pubsub_v1/services/publisher/transports/grpc.py index 9e8ed1737..e192152d8 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc.py +++ b/google/pubsub_v1/services/publisher/transports/grpc.py @@ -155,9 +155,10 @@ def __init__( are specified, the client will attempt to ascertain the credentials from the environment. This argument is ignored if a ``channel`` instance is provided. - credentials_file (Optional[str]): A file with credentials that can + credentials_file (Optional[str]): Deprecated. A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. This argument is ignored if a ``channel`` instance is provided. + This argument will be removed in the next major version of this library. scopes (Optional(Sequence[str])): A list of scopes. This argument is ignored if a ``channel`` instance is provided. channel (Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]]): @@ -292,9 +293,10 @@ def create_channel( credentials identify this application to the service. If none are specified, the client will attempt to ascertain the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can + credentials_file (Optional[str]): Deprecated. A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is mutually exclusive with credentials. + This argument is mutually exclusive with credentials. This argument will be + removed in the next major version of this library. scopes (Optional[Sequence[str]]): A optional list of scopes needed for this service. These are only used when credentials are not specified and are passed to :func:`google.auth.default`. diff --git a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py index 90683cbd0..14b9fdd06 100644 --- a/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/publisher/transports/grpc_asyncio.py @@ -152,8 +152,9 @@ def create_channel( credentials identify this application to the service. If none are specified, the client will attempt to ascertain the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can - be loaded with :func:`google.auth.load_credentials_from_file`. + credentials_file (Optional[str]): Deprecated. A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. This argument will be + removed in the next major version of this library. scopes (Optional[Sequence[str]]): A optional list of scopes needed for this service. These are only used when credentials are not specified and are passed to :func:`google.auth.default`. @@ -204,9 +205,10 @@ def __init__( are specified, the client will attempt to ascertain the credentials from the environment. This argument is ignored if a ``channel`` instance is provided. - credentials_file (Optional[str]): A file with credentials that can + credentials_file (Optional[str]): Deprecated. A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. This argument is ignored if a ``channel`` instance is provided. + This argument will be removed in the next major version of this library. scopes (Optional[Sequence[str]]): A optional list of scopes needed for this service. These are only used when credentials are not specified and are passed to :func:`google.auth.default`. diff --git a/google/pubsub_v1/services/publisher/transports/rest.py b/google/pubsub_v1/services/publisher/transports/rest.py index 92ee46e15..aeb07184c 100644 --- a/google/pubsub_v1/services/publisher/transports/rest.py +++ b/google/pubsub_v1/services/publisher/transports/rest.py @@ -650,9 +650,10 @@ def __init__( are specified, the client will attempt to ascertain the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can + credentials_file (Optional[str]): Deprecated. A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. + This argument is ignored if ``channel`` is provided. This argument will be + removed in the next major version of this library. scopes (Optional(Sequence[str])): A list of scopes. This argument is ignored if ``channel`` is provided. client_cert_source_for_mtls (Callable[[], Tuple[bytes, bytes]]): Client diff --git a/google/pubsub_v1/services/schema_service/transports/base.py b/google/pubsub_v1/services/schema_service/transports/base.py index c30773b74..bfe254e0a 100644 --- a/google/pubsub_v1/services/schema_service/transports/base.py +++ b/google/pubsub_v1/services/schema_service/transports/base.py @@ -74,9 +74,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can + credentials_file (Optional[str]): Deprecated. A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is mutually exclusive with credentials. + This argument is mutually exclusive with credentials. This argument will be + removed in the next major version of this library. scopes (Optional[Sequence[str]]): A list of scopes. quota_project_id (Optional[str]): An optional project to use for billing and quota. diff --git a/google/pubsub_v1/services/schema_service/transports/grpc.py b/google/pubsub_v1/services/schema_service/transports/grpc.py index 1a746ef43..5bcfd8b9b 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc.py @@ -155,9 +155,10 @@ def __init__( are specified, the client will attempt to ascertain the credentials from the environment. This argument is ignored if a ``channel`` instance is provided. - credentials_file (Optional[str]): A file with credentials that can + credentials_file (Optional[str]): Deprecated. A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. This argument is ignored if a ``channel`` instance is provided. + This argument will be removed in the next major version of this library. scopes (Optional(Sequence[str])): A list of scopes. This argument is ignored if a ``channel`` instance is provided. channel (Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]]): @@ -292,9 +293,10 @@ def create_channel( credentials identify this application to the service. If none are specified, the client will attempt to ascertain the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can + credentials_file (Optional[str]): Deprecated. A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is mutually exclusive with credentials. + This argument is mutually exclusive with credentials. This argument will be + removed in the next major version of this library. scopes (Optional[Sequence[str]]): A optional list of scopes needed for this service. These are only used when credentials are not specified and are passed to :func:`google.auth.default`. diff --git a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py index f030d0563..ac2980ded 100644 --- a/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/schema_service/transports/grpc_asyncio.py @@ -152,8 +152,9 @@ def create_channel( credentials identify this application to the service. If none are specified, the client will attempt to ascertain the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can - be loaded with :func:`google.auth.load_credentials_from_file`. + credentials_file (Optional[str]): Deprecated. A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. This argument will be + removed in the next major version of this library. scopes (Optional[Sequence[str]]): A optional list of scopes needed for this service. These are only used when credentials are not specified and are passed to :func:`google.auth.default`. @@ -204,9 +205,10 @@ def __init__( are specified, the client will attempt to ascertain the credentials from the environment. This argument is ignored if a ``channel`` instance is provided. - credentials_file (Optional[str]): A file with credentials that can + credentials_file (Optional[str]): Deprecated. A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. This argument is ignored if a ``channel`` instance is provided. + This argument will be removed in the next major version of this library. scopes (Optional[Sequence[str]]): A optional list of scopes needed for this service. These are only used when credentials are not specified and are passed to :func:`google.auth.default`. diff --git a/google/pubsub_v1/services/schema_service/transports/rest.py b/google/pubsub_v1/services/schema_service/transports/rest.py index 6d8902c5d..a0d42c2dd 100644 --- a/google/pubsub_v1/services/schema_service/transports/rest.py +++ b/google/pubsub_v1/services/schema_service/transports/rest.py @@ -702,9 +702,10 @@ def __init__( are specified, the client will attempt to ascertain the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can + credentials_file (Optional[str]): Deprecated. A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. + This argument is ignored if ``channel`` is provided. This argument will be + removed in the next major version of this library. scopes (Optional(Sequence[str])): A list of scopes. This argument is ignored if ``channel`` is provided. client_cert_source_for_mtls (Callable[[], Tuple[bytes, bytes]]): Client diff --git a/google/pubsub_v1/services/subscriber/async_client.py b/google/pubsub_v1/services/subscriber/async_client.py index 77f96d6df..34843a0e0 100644 --- a/google/pubsub_v1/services/subscriber/async_client.py +++ b/google/pubsub_v1/services/subscriber/async_client.py @@ -86,6 +86,8 @@ class SubscriberAsyncClient: _DEFAULT_ENDPOINT_TEMPLATE = SubscriberClient._DEFAULT_ENDPOINT_TEMPLATE _DEFAULT_UNIVERSE = SubscriberClient._DEFAULT_UNIVERSE + listing_path = staticmethod(SubscriberClient.listing_path) + parse_listing_path = staticmethod(SubscriberClient.parse_listing_path) snapshot_path = staticmethod(SubscriberClient.snapshot_path) parse_snapshot_path = staticmethod(SubscriberClient.parse_snapshot_path) subscription_path = staticmethod(SubscriberClient.subscription_path) diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index d928d3678..98b11e8c3 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -219,6 +219,30 @@ def transport(self) -> SubscriberTransport: """ return self._transport + @staticmethod + def listing_path( + project: str, + location: str, + data_exchange: str, + listing: str, + ) -> str: + """Returns a fully-qualified listing string.""" + return "projects/{project}/locations/{location}/dataExchanges/{data_exchange}/listings/{listing}".format( + project=project, + location=location, + data_exchange=data_exchange, + listing=listing, + ) + + @staticmethod + def parse_listing_path(path: str) -> Dict[str, str]: + """Parses a listing path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/dataExchanges/(?P.+?)/listings/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + @staticmethod def snapshot_path( project: str, diff --git a/google/pubsub_v1/services/subscriber/transports/base.py b/google/pubsub_v1/services/subscriber/transports/base.py index cc62113f9..a25ff562f 100644 --- a/google/pubsub_v1/services/subscriber/transports/base.py +++ b/google/pubsub_v1/services/subscriber/transports/base.py @@ -73,9 +73,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can + credentials_file (Optional[str]): Deprecated. A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is mutually exclusive with credentials. + This argument is mutually exclusive with credentials. This argument will be + removed in the next major version of this library. scopes (Optional[Sequence[str]]): A list of scopes. quota_project_id (Optional[str]): An optional project to use for billing and quota. diff --git a/google/pubsub_v1/services/subscriber/transports/grpc.py b/google/pubsub_v1/services/subscriber/transports/grpc.py index 36c77648b..705163791 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc.py @@ -157,9 +157,10 @@ def __init__( are specified, the client will attempt to ascertain the credentials from the environment. This argument is ignored if a ``channel`` instance is provided. - credentials_file (Optional[str]): A file with credentials that can + credentials_file (Optional[str]): Deprecated. A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. This argument is ignored if a ``channel`` instance is provided. + This argument will be removed in the next major version of this library. scopes (Optional(Sequence[str])): A list of scopes. This argument is ignored if a ``channel`` instance is provided. channel (Optional[Union[grpc.Channel, Callable[..., grpc.Channel]]]): @@ -294,9 +295,10 @@ def create_channel( credentials identify this application to the service. If none are specified, the client will attempt to ascertain the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can + credentials_file (Optional[str]): Deprecated. A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is mutually exclusive with credentials. + This argument is mutually exclusive with credentials. This argument will be + removed in the next major version of this library. scopes (Optional[Sequence[str]]): A optional list of scopes needed for this service. These are only used when credentials are not specified and are passed to :func:`google.auth.default`. diff --git a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py index 78f8afd4c..ad53fe76c 100644 --- a/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py +++ b/google/pubsub_v1/services/subscriber/transports/grpc_asyncio.py @@ -154,8 +154,9 @@ def create_channel( credentials identify this application to the service. If none are specified, the client will attempt to ascertain the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can - be loaded with :func:`google.auth.load_credentials_from_file`. + credentials_file (Optional[str]): Deprecated. A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. This argument will be + removed in the next major version of this library. scopes (Optional[Sequence[str]]): A optional list of scopes needed for this service. These are only used when credentials are not specified and are passed to :func:`google.auth.default`. @@ -206,9 +207,10 @@ def __init__( are specified, the client will attempt to ascertain the credentials from the environment. This argument is ignored if a ``channel`` instance is provided. - credentials_file (Optional[str]): A file with credentials that can + credentials_file (Optional[str]): Deprecated. A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. This argument is ignored if a ``channel`` instance is provided. + This argument will be removed in the next major version of this library. scopes (Optional[Sequence[str]]): A optional list of scopes needed for this service. These are only used when credentials are not specified and are passed to :func:`google.auth.default`. diff --git a/google/pubsub_v1/services/subscriber/transports/rest.py b/google/pubsub_v1/services/subscriber/transports/rest.py index bb0ee68c6..50a247cef 100644 --- a/google/pubsub_v1/services/subscriber/transports/rest.py +++ b/google/pubsub_v1/services/subscriber/transports/rest.py @@ -828,9 +828,10 @@ def __init__( are specified, the client will attempt to ascertain the credentials from the environment. - credentials_file (Optional[str]): A file with credentials that can + credentials_file (Optional[str]): Deprecated. A file with credentials that can be loaded with :func:`google.auth.load_credentials_from_file`. - This argument is ignored if ``channel`` is provided. + This argument is ignored if ``channel`` is provided. This argument will be + removed in the next major version of this library. scopes (Optional(Sequence[str])): A list of scopes. This argument is ignored if ``channel`` is provided. client_cert_source_for_mtls (Callable[[], Tuple[bytes, bytes]]): Client diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index 9fc8f87eb..26c13fb18 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -1210,6 +1210,13 @@ class AwsKinesisFailureReason(proto.Message): Optional. Failure encountered when applying a message transformation to the Pub/Sub message. + This field is a member of `oneof`_ ``reason``. + api_violation_reason (google.pubsub_v1.types.IngestionFailureEvent.ApiViolationReason): + Optional. The message failed to be published + due to an API violation. This is only set when + the size of the data field of the Kinesis record + is zero. + This field is a member of `oneof`_ ``reason``. """ @@ -1239,6 +1246,12 @@ class AwsKinesisFailureReason(proto.Message): oneof="reason", message="IngestionFailureEvent.MessageTransformationFailureReason", ) + api_violation_reason: "IngestionFailureEvent.ApiViolationReason" = proto.Field( + proto.MESSAGE, + number=6, + oneof="reason", + message="IngestionFailureEvent.ApiViolationReason", + ) topic: str = proto.Field( proto.STRING, @@ -1423,6 +1436,13 @@ class Topic(proto.Message): Optional. Transforms to be applied to messages published to the topic. Transforms are applied in the order specified. + tags (MutableMapping[str, str]): + Optional. Input only. Immutable. Tag + keys/values directly bound to this resource. For + example: + + "123/environment": "production", + "123/costCenter": "marketing". """ class State(proto.Enum): @@ -1491,6 +1511,11 @@ class State(proto.Enum): number=13, message="MessageTransform", ) + tags: MutableMapping[str, str] = proto.MapField( + proto.STRING, + proto.STRING, + number=14, + ) class PubsubMessage(proto.Message): @@ -2024,6 +2049,13 @@ class Subscription(proto.Message): messages before they are delivered to subscribers. Transforms are applied in the order specified. + tags (MutableMapping[str, str]): + Optional. Input only. Immutable. Tag + keys/values directly bound to this resource. For + example: + + "123/environment": "production", + "123/costCenter": "marketing". """ class State(proto.Enum): @@ -2162,6 +2194,11 @@ class AnalyticsHubSubscriptionInfo(proto.Message): number=25, message="MessageTransform", ) + tags: MutableMapping[str, str] = proto.MapField( + proto.STRING, + proto.STRING, + number=26, + ) class RetryPolicy(proto.Message): @@ -3161,8 +3198,7 @@ class StreamingPullResponse(proto.Message): Attributes: received_messages (MutableSequence[google.pubsub_v1.types.ReceivedMessage]): - Optional. Received Pub/Sub messages. This - will not be empty. + Optional. Received Pub/Sub messages. acknowledge_confirmation (google.pubsub_v1.types.StreamingPullResponse.AcknowledgeConfirmation): Optional. This field will only be set if ``enable_exactly_once_delivery`` is set to ``true`` and is @@ -3312,6 +3348,13 @@ class CreateSnapshotRequest(proto.Message): labels (MutableMapping[str, str]): Optional. See `Creating and managing labels `__. + tags (MutableMapping[str, str]): + Optional. Input only. Immutable. Tag + keys/values directly bound to this resource. For + example: + + "123/environment": "production", + "123/costCenter": "marketing". """ name: str = proto.Field( @@ -3327,6 +3370,11 @@ class CreateSnapshotRequest(proto.Message): proto.STRING, number=3, ) + tags: MutableMapping[str, str] = proto.MapField( + proto.STRING, + proto.STRING, + number=4, + ) class UpdateSnapshotRequest(proto.Message): diff --git a/scripts/fixup_pubsub_v1_keywords.py b/scripts/fixup_pubsub_v1_keywords.py index 9f5619e8c..e4c132570 100644 --- a/scripts/fixup_pubsub_v1_keywords.py +++ b/scripts/fixup_pubsub_v1_keywords.py @@ -42,9 +42,9 @@ class pubsubCallTransformer(cst.CSTTransformer): 'acknowledge': ('subscription', 'ack_ids', ), 'commit_schema': ('name', 'schema', ), 'create_schema': ('parent', 'schema', 'schema_id', ), - 'create_snapshot': ('name', 'subscription', 'labels', ), - 'create_subscription': ('name', 'topic', 'push_config', 'bigquery_config', 'cloud_storage_config', 'ack_deadline_seconds', 'retain_acked_messages', 'message_retention_duration', 'labels', 'enable_message_ordering', 'expiration_policy', 'filter', 'dead_letter_policy', 'retry_policy', 'detached', 'enable_exactly_once_delivery', 'topic_message_retention_duration', 'state', 'analytics_hub_subscription_info', 'message_transforms', ), - 'create_topic': ('name', 'labels', 'message_storage_policy', 'kms_key_name', 'schema_settings', 'satisfies_pzs', 'message_retention_duration', 'state', 'ingestion_data_source_settings', 'message_transforms', ), + 'create_snapshot': ('name', 'subscription', 'labels', 'tags', ), + 'create_subscription': ('name', 'topic', 'push_config', 'bigquery_config', 'cloud_storage_config', 'ack_deadline_seconds', 'retain_acked_messages', 'message_retention_duration', 'labels', 'enable_message_ordering', 'expiration_policy', 'filter', 'dead_letter_policy', 'retry_policy', 'detached', 'enable_exactly_once_delivery', 'topic_message_retention_duration', 'state', 'analytics_hub_subscription_info', 'message_transforms', 'tags', ), + 'create_topic': ('name', 'labels', 'message_storage_policy', 'kms_key_name', 'schema_settings', 'satisfies_pzs', 'message_retention_duration', 'state', 'ingestion_data_source_settings', 'message_transforms', 'tags', ), 'delete_schema': ('name', ), 'delete_schema_revision': ('name', 'revision_id', ), 'delete_snapshot': ('snapshot', ), diff --git a/testing/constraints-3.10.txt b/testing/constraints-3.10.txt index ad3f0fa58..ef1c92fff 100644 --- a/testing/constraints-3.10.txt +++ b/testing/constraints-3.10.txt @@ -2,6 +2,8 @@ # This constraints file is required for unit tests. # List all library dependencies and extras in this file. google-api-core +google-auth +grpcio proto-plus protobuf grpc-google-iam-v1 diff --git a/testing/constraints-3.11.txt b/testing/constraints-3.11.txt index ad3f0fa58..ef1c92fff 100644 --- a/testing/constraints-3.11.txt +++ b/testing/constraints-3.11.txt @@ -2,6 +2,8 @@ # This constraints file is required for unit tests. # List all library dependencies and extras in this file. google-api-core +google-auth +grpcio proto-plus protobuf grpc-google-iam-v1 diff --git a/testing/constraints-3.12.txt b/testing/constraints-3.12.txt index ad3f0fa58..ef1c92fff 100644 --- a/testing/constraints-3.12.txt +++ b/testing/constraints-3.12.txt @@ -2,6 +2,8 @@ # This constraints file is required for unit tests. # List all library dependencies and extras in this file. google-api-core +google-auth +grpcio proto-plus protobuf grpc-google-iam-v1 diff --git a/testing/constraints-3.13.txt b/testing/constraints-3.13.txt index 2010e549c..2ae5a677e 100644 --- a/testing/constraints-3.13.txt +++ b/testing/constraints-3.13.txt @@ -7,6 +7,7 @@ # Then this file should have google-cloud-foo>=1 google-api-core>=2 google-auth>=2 +grpcio>=1 proto-plus>=1 protobuf>=6 grpc-google-iam-v1>=0 diff --git a/testing/constraints-3.9.txt b/testing/constraints-3.9.txt index ad3f0fa58..ef1c92fff 100644 --- a/testing/constraints-3.9.txt +++ b/testing/constraints-3.9.txt @@ -2,6 +2,8 @@ # This constraints file is required for unit tests. # List all library dependencies and extras in this file. google-api-core +google-auth +grpcio proto-plus protobuf grpc-google-iam-v1 diff --git a/tests/unit/gapic/pubsub_v1/test_publisher.py b/tests/unit/gapic/pubsub_v1/test_publisher.py index 223679ad1..6748ac09a 100644 --- a/tests/unit/gapic/pubsub_v1/test_publisher.py +++ b/tests/unit/gapic/pubsub_v1/test_publisher.py @@ -8898,9 +8898,38 @@ def test_publisher_transport_channel_mtls_with_adc(transport_class): assert transport.grpc_channel == mock_grpc_channel -def test_schema_path(): +def test_crypto_key_path(): project = "squid" - schema = "clam" + location = "clam" + key_ring = "whelk" + crypto_key = "octopus" + expected = "projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{crypto_key}".format( + project=project, + location=location, + key_ring=key_ring, + crypto_key=crypto_key, + ) + actual = PublisherClient.crypto_key_path(project, location, key_ring, crypto_key) + assert expected == actual + + +def test_parse_crypto_key_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "key_ring": "cuttlefish", + "crypto_key": "mussel", + } + path = PublisherClient.crypto_key_path(**expected) + + # Check that the path construction is reversible. + actual = PublisherClient.parse_crypto_key_path(path) + assert expected == actual + + +def test_schema_path(): + project = "winkle" + schema = "nautilus" expected = "projects/{project}/schemas/{schema}".format( project=project, schema=schema, @@ -8911,8 +8940,8 @@ def test_schema_path(): def test_parse_schema_path(): expected = { - "project": "whelk", - "schema": "octopus", + "project": "scallop", + "schema": "abalone", } path = PublisherClient.schema_path(**expected) @@ -8921,6 +8950,29 @@ def test_parse_schema_path(): assert expected == actual +def test_snapshot_path(): + project = "squid" + snapshot = "clam" + expected = "projects/{project}/snapshots/{snapshot}".format( + project=project, + snapshot=snapshot, + ) + actual = PublisherClient.snapshot_path(project, snapshot) + assert expected == actual + + +def test_parse_snapshot_path(): + expected = { + "project": "whelk", + "snapshot": "octopus", + } + path = PublisherClient.snapshot_path(**expected) + + # Check that the path construction is reversible. + actual = PublisherClient.parse_snapshot_path(path) + assert expected == actual + + def test_subscription_path(): project = "oyster" subscription = "nudibranch" diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py index 417249e97..9186c815a 100644 --- a/tests/unit/gapic/pubsub_v1/test_subscriber.py +++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py @@ -13208,9 +13208,38 @@ def test_subscriber_transport_channel_mtls_with_adc(transport_class): assert transport.grpc_channel == mock_grpc_channel -def test_snapshot_path(): +def test_listing_path(): project = "squid" - snapshot = "clam" + location = "clam" + data_exchange = "whelk" + listing = "octopus" + expected = "projects/{project}/locations/{location}/dataExchanges/{data_exchange}/listings/{listing}".format( + project=project, + location=location, + data_exchange=data_exchange, + listing=listing, + ) + actual = SubscriberClient.listing_path(project, location, data_exchange, listing) + assert expected == actual + + +def test_parse_listing_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "data_exchange": "cuttlefish", + "listing": "mussel", + } + path = SubscriberClient.listing_path(**expected) + + # Check that the path construction is reversible. + actual = SubscriberClient.parse_listing_path(path) + assert expected == actual + + +def test_snapshot_path(): + project = "winkle" + snapshot = "nautilus" expected = "projects/{project}/snapshots/{snapshot}".format( project=project, snapshot=snapshot, @@ -13221,8 +13250,8 @@ def test_snapshot_path(): def test_parse_snapshot_path(): expected = { - "project": "whelk", - "snapshot": "octopus", + "project": "scallop", + "snapshot": "abalone", } path = SubscriberClient.snapshot_path(**expected) @@ -13232,8 +13261,8 @@ def test_parse_snapshot_path(): def test_subscription_path(): - project = "oyster" - subscription = "nudibranch" + project = "squid" + subscription = "clam" expected = "projects/{project}/subscriptions/{subscription}".format( project=project, subscription=subscription, @@ -13244,8 +13273,8 @@ def test_subscription_path(): def test_parse_subscription_path(): expected = { - "project": "cuttlefish", - "subscription": "mussel", + "project": "whelk", + "subscription": "octopus", } path = SubscriberClient.subscription_path(**expected) @@ -13255,8 +13284,8 @@ def test_parse_subscription_path(): def test_topic_path(): - project = "winkle" - topic = "nautilus" + project = "oyster" + topic = "nudibranch" expected = "projects/{project}/topics/{topic}".format( project=project, topic=topic, @@ -13267,8 +13296,8 @@ def test_topic_path(): def test_parse_topic_path(): expected = { - "project": "scallop", - "topic": "abalone", + "project": "cuttlefish", + "topic": "mussel", } path = SubscriberClient.topic_path(**expected) @@ -13278,7 +13307,7 @@ def test_parse_topic_path(): def test_common_billing_account_path(): - billing_account = "squid" + billing_account = "winkle" expected = "billingAccounts/{billing_account}".format( billing_account=billing_account, ) @@ -13288,7 +13317,7 @@ def test_common_billing_account_path(): def test_parse_common_billing_account_path(): expected = { - "billing_account": "clam", + "billing_account": "nautilus", } path = SubscriberClient.common_billing_account_path(**expected) @@ -13298,7 +13327,7 @@ def test_parse_common_billing_account_path(): def test_common_folder_path(): - folder = "whelk" + folder = "scallop" expected = "folders/{folder}".format( folder=folder, ) @@ -13308,7 +13337,7 @@ def test_common_folder_path(): def test_parse_common_folder_path(): expected = { - "folder": "octopus", + "folder": "abalone", } path = SubscriberClient.common_folder_path(**expected) @@ -13318,7 +13347,7 @@ def test_parse_common_folder_path(): def test_common_organization_path(): - organization = "oyster" + organization = "squid" expected = "organizations/{organization}".format( organization=organization, ) @@ -13328,7 +13357,7 @@ def test_common_organization_path(): def test_parse_common_organization_path(): expected = { - "organization": "nudibranch", + "organization": "clam", } path = SubscriberClient.common_organization_path(**expected) @@ -13338,7 +13367,7 @@ def test_parse_common_organization_path(): def test_common_project_path(): - project = "cuttlefish" + project = "whelk" expected = "projects/{project}".format( project=project, ) @@ -13348,7 +13377,7 @@ def test_common_project_path(): def test_parse_common_project_path(): expected = { - "project": "mussel", + "project": "octopus", } path = SubscriberClient.common_project_path(**expected) @@ -13358,8 +13387,8 @@ def test_parse_common_project_path(): def test_common_location_path(): - project = "winkle" - location = "nautilus" + project = "oyster" + location = "nudibranch" expected = "projects/{project}/locations/{location}".format( project=project, location=location, @@ -13370,8 +13399,8 @@ def test_common_location_path(): def test_parse_common_location_path(): expected = { - "project": "scallop", - "location": "abalone", + "project": "cuttlefish", + "location": "mussel", } path = SubscriberClient.common_location_path(**expected) From c44bbbdbf27de8e14fa9508c26ce9c9b38eebad3 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 13:07:09 -0700 Subject: [PATCH 175/190] chore(main): release 2.33.0 (#1553) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 19 +++++++++++++++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7bc1375d5..7d35f2a0f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,4 +1,4 @@ { - ".": "2.32.0" + ".": "2.33.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index cbe6cea28..ac71546a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,25 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.33.0](https://github.com/googleapis/python-pubsub/compare/v2.32.0...v2.33.0) (2025-10-30) + + +### Features + +* Add AwsKinesisFailureReason.ApiViolationReason ([ac68093](https://github.com/googleapis/python-pubsub/commit/ac6809350758306f28fa1ab46939bc438b5a5e19)) +* Add tags to Subscription, Topic, and CreateSnapshotRequest messages for use in CreateSubscription, CreateTopic, and CreateSnapshot requests respectively ([ac68093](https://github.com/googleapis/python-pubsub/commit/ac6809350758306f28fa1ab46939bc438b5a5e19)) +* Annotate some resource fields with their corresponding API types ([ac68093](https://github.com/googleapis/python-pubsub/commit/ac6809350758306f28fa1ab46939bc438b5a5e19)) + + +### Bug Fixes + +* Deprecate credentials_file argument ([ac68093](https://github.com/googleapis/python-pubsub/commit/ac6809350758306f28fa1ab46939bc438b5a5e19)) + + +### Documentation + +* A comment for field `received_messages` in message `.google.pubsub.v1.StreamingPullResponse` is changed ([ac68093](https://github.com/googleapis/python-pubsub/commit/ac6809350758306f28fa1ab46939bc438b5a5e19)) + ## [2.32.0](https://github.com/googleapis/python-pubsub/compare/v2.31.1...v2.32.0) (2025-10-28) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 3c958586f..0c5de5c03 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.32.0" # {x-release-please-version} +__version__ = "2.33.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 3c958586f..0c5de5c03 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.32.0" # {x-release-please-version} +__version__ = "2.33.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index 23d997304..bcad2272f 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.32.0" + "version": "2.33.0" }, "snippets": [ { From ac6cc2577837354643b6c8fbb1903f95b50151d6 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Thu, 30 Oct 2025 13:27:25 -0700 Subject: [PATCH 176/190] chore: cleanup owlbot and release please files (#1548) This PR cleans up files for owl bot post processor and release please. --- .github/.OwlBot.lock.yaml | 17 ----------------- .github/.OwlBot.yaml | 26 -------------------------- .github/release-please.yml | 12 ------------ .github/release-trigger.yml | 2 -- .librarian/state.yaml | 2 +- 5 files changed, 1 insertion(+), 58 deletions(-) delete mode 100644 .github/.OwlBot.lock.yaml delete mode 100644 .github/.OwlBot.yaml delete mode 100644 .github/release-please.yml delete mode 100644 .github/release-trigger.yml diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml deleted file mode 100644 index 4a311db02..000000000 --- a/.github/.OwlBot.lock.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2025 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. -docker: - image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:543e209e7c1c1ffe720eb4db1a3f045a75099304fb19aa11a47dc717b8aae2a9 -# created: 2025-10-09T14:48:42.914384887Z diff --git a/.github/.OwlBot.yaml b/.github/.OwlBot.yaml deleted file mode 100644 index 0bfe82f74..000000000 --- a/.github/.OwlBot.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2021 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. - -docker: - image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - -deep-remove-regex: - - /owl-bot-staging - -deep-copy-regex: - - source: /google/pubsub/(v.*)/.*-py/(.*) - dest: /owl-bot-staging/$1/$2 - -begin-after-commit-hash: 40278112d2922ec917140dcb5cc6d5ef2923aeb2 - diff --git a/.github/release-please.yml b/.github/release-please.yml deleted file mode 100644 index fe749ff6b..000000000 --- a/.github/release-please.yml +++ /dev/null @@ -1,12 +0,0 @@ -releaseType: python -handleGHRelease: true -manifest: true -# NOTE: this section is generated by synthtool.languages.python -# See https://github.com/googleapis/synthtool/blob/master/synthtool/languages/python.py -branches: -- branch: v1 - handleGHRelease: true - releaseType: python -- branch: v0 - handleGHRelease: true - releaseType: python diff --git a/.github/release-trigger.yml b/.github/release-trigger.yml deleted file mode 100644 index aa0d30a3e..000000000 --- a/.github/release-trigger.yml +++ /dev/null @@ -1,2 +0,0 @@ -enabled: true -multiScmName: python-pubsub diff --git a/.librarian/state.yaml b/.librarian/state.yaml index deaa22de7..4d7702c55 100644 --- a/.librarian/state.yaml +++ b/.librarian/state.yaml @@ -1,7 +1,7 @@ image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator:latest libraries: - id: google-cloud-pubsub - version: 2.31.1 + version: 2.32.0 apis: [] source_roots: - . From e4b86c9339423f10b365ce3f930cecb8bc6c81b6 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Thu, 30 Oct 2025 17:05:03 -0400 Subject: [PATCH 177/190] chore(librarian): remove release please configuration files (#1554) Towards https://github.com/googleapis/librarian/issues/2457 --- .release-please-manifest.json | 4 ---- release-please-config.json | 25 ------------------------- 2 files changed, 29 deletions(-) delete mode 100644 .release-please-manifest.json delete mode 100644 release-please-config.json diff --git a/.release-please-manifest.json b/.release-please-manifest.json deleted file mode 100644 index 7d35f2a0f..000000000 --- a/.release-please-manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - ".": "2.33.0" -} - \ No newline at end of file diff --git a/release-please-config.json b/release-please-config.json deleted file mode 100644 index 909352415..000000000 --- a/release-please-config.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", - "packages": { - ".": { - "release-type": "python", - "extra-files": [ - "google/pubsub/gapic_version.py", - "google/pubsub_v1/gapic_version.py", - { - "type": "json", - "path": "samples/generated_samples/snippet_metadata_google.pubsub.v1.json", - "jsonpath": "$.clientLibrary.version" - } - ] - } - }, - "release-type": "python", - "plugins": [ - { - "type": "sentence-case" - } - ], - "initial-version": "0.1.0" -} - \ No newline at end of file From 78418d48f4bd54bf043dd1844558a0ae2e0037b8 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Wed, 5 Nov 2025 05:12:40 -0500 Subject: [PATCH 178/190] chore(librarian): onboard to librarian, including generate (#1552) Towards https://github.com/googleapis/librarian/issues/2457 --- .github/workflows/lint.yml | 2 +- .github/workflows/unittest.yml | 2 +- .../generator-input/.repo-metadata.json | 18 + .librarian/generator-input/noxfile.py | 556 ++++++++++++++++++ .librarian/generator-input/owlbot.py | 352 +++++++++++ .librarian/generator-input/setup.py | 104 ++++ .librarian/state.yaml | 12 +- google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- noxfile.py | 4 +- owlbot.py | 2 +- testing/constraints-3.14.txt | 2 +- 12 files changed, 1047 insertions(+), 11 deletions(-) create mode 100644 .librarian/generator-input/.repo-metadata.json create mode 100644 .librarian/generator-input/noxfile.py create mode 100644 .librarian/generator-input/owlbot.py create mode 100644 .librarian/generator-input/setup.py diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 46a3ff38f..6204983fd 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v6 with: - python-version: "3.13" + python-version: "3.14" - name: Install nox run: | python -m pip install --upgrade setuptools pip wheel diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index d59bbb1b8..04e411304 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -45,7 +45,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 with: - python-version: "3.8" + python-version: "3.14" - name: Install coverage run: | python -m pip install --upgrade setuptools pip wheel diff --git a/.librarian/generator-input/.repo-metadata.json b/.librarian/generator-input/.repo-metadata.json new file mode 100644 index 000000000..8d12e4cc0 --- /dev/null +++ b/.librarian/generator-input/.repo-metadata.json @@ -0,0 +1,18 @@ +{ + "name": "pubsub", + "name_pretty": "Google Cloud Pub/Sub", + "product_documentation": "https://cloud.google.com/pubsub/docs/", + "client_documentation": "https://cloud.google.com/python/docs/reference/pubsub/latest", + "issue_tracker": "https://issuetracker.google.com/savedsearches/559741", + "release_level": "stable", + "language": "python", + "repo": "googleapis/python-pubsub", + "distribution_name": "google-cloud-pubsub", + "api_id": "pubsub.googleapis.com", + "requires_billing": true, + "default_version": "v1", + "codeowner_team": "@googleapis/api-pubsub", + "api_shortname": "pubsub", + "library_type": "GAPIC_COMBO", + "api_description": "is designed to provide reliable, many-to-many, asynchronous messaging between applications. Publisher applications can send messages to a topic and other applications can subscribe to that topic to receive the messages. By decoupling senders and receivers, Google Cloud Pub/Sub allows developers to communicate between independently written applications." +} diff --git a/.librarian/generator-input/noxfile.py b/.librarian/generator-input/noxfile.py new file mode 100644 index 000000000..fd552166c --- /dev/null +++ b/.librarian/generator-input/noxfile.py @@ -0,0 +1,556 @@ +# -*- coding: utf-8 -*- +# +# 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. +# 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. + +# Generated by synthtool. DO NOT EDIT! + +from __future__ import absolute_import + +import os +import pathlib +import re +import shutil +from typing import Dict, List +import warnings + +import nox + +FLAKE8_VERSION = "flake8==6.1.0" +BLACK_VERSION = "black[jupyter]==23.7.0" +ISORT_VERSION = "isort==5.11.0" +LINT_PATHS = ["google", "tests", "noxfile.py", "setup.py"] + +MYPY_VERSION = "mypy==1.10.0" + +DEFAULT_PYTHON_VERSION = "3.14" + +UNIT_TEST_PYTHON_VERSIONS: List[str] = [ + "3.7", + "3.8", + "3.9", + "3.10", + "3.11", + "3.12", + "3.13", + "3.14", +] +UNIT_TEST_STANDARD_DEPENDENCIES = [ + "mock", + "asyncmock", + "pytest", + "pytest-cov", + "pytest-asyncio", +] +UNIT_TEST_EXTERNAL_DEPENDENCIES: List[str] = [] +UNIT_TEST_LOCAL_DEPENDENCIES: List[str] = [] +UNIT_TEST_DEPENDENCIES: List[str] = [ + "flaky", +] +UNIT_TEST_EXTRAS: List[str] = [] +UNIT_TEST_EXTRAS_BY_PYTHON: Dict[str, List[str]] = {} + +SYSTEM_TEST_PYTHON_VERSIONS: List[str] = ["3.12"] +SYSTEM_TEST_STANDARD_DEPENDENCIES: List[str] = [ + "mock", + "pytest", + "google-cloud-testutils", +] +SYSTEM_TEST_EXTERNAL_DEPENDENCIES: List[str] = [ + "psutil", + "flaky", +] +SYSTEM_TEST_LOCAL_DEPENDENCIES: List[str] = [] +SYSTEM_TEST_DEPENDENCIES: List[str] = [] +SYSTEM_TEST_EXTRAS: List[str] = [] +SYSTEM_TEST_EXTRAS_BY_PYTHON: Dict[str, List[str]] = {} + +CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() + +nox.options.sessions = [ + "unit", + "system", + "cover", + "lint", + "lint_setup_py", + "blacken", + "mypy", + # https://github.com/googleapis/python-pubsub/pull/552#issuecomment-1016256936 + # "mypy_samples", # TODO: uncomment when the check passes + "docs", + "docfx", + "format", +] + +# Error if a python version is missing +nox.options.error_on_missing_interpreters = True + + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def mypy(session): + """Run type checks with mypy.""" + session.install("-e", ".[all]") + session.install(MYPY_VERSION) + + # Version 2.1.1 of google-api-core version is the first type-checked release. + # Version 2.2.0 of google-cloud-core version is the first type-checked release. + session.install( + "google-api-core[grpc]>=2.1.1", "google-cloud-core>=2.2.0", "types-requests" + ) + + # Just install the type info directly, since "mypy --install-types" might + # require an additional pass. + # Exclude types-protobuf==4.24.0.20240106 + # See https://github.com/python/typeshed/issues/11254 + session.install("types-protobuf!=4.24.0.20240106", "types-setuptools") + + # TODO: Only check the hand-written layer, the generated code does not pass + # mypy checks yet. + # https://github.com/googleapis/gapic-generator-python/issues/1092 + # TODO: Re-enable mypy checks once we merge, since incremental checks are failing due to protobuf upgrade + # session.run("mypy", "-p", "google.cloud", "--exclude", "google/pubsub_v1/") + + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def mypy_samples(session): + """Run type checks with mypy.""" + + session.install("-e", ".[all]") + + session.install("pytest") + session.install(MYPY_VERSION) + + # Just install the type info directly, since "mypy --install-types" might + # require an additional pass. + session.install( + "types-mock", "types-protobuf", "types-setuptools", "types-requests" + ) + + session.run( + "mypy", + "--config-file", + str(CURRENT_DIRECTORY / "samples" / "snippets" / "mypy.ini"), + "--no-incremental", # Required by warn-unused-configs from mypy.ini to work + "samples/", + ) + + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def lint(session): + """Run linters. + + Returns a failure if the linters find linting errors or sufficiently + serious code quality issues. + """ + session.install(FLAKE8_VERSION, BLACK_VERSION) + session.run( + "black", + "--check", + *LINT_PATHS, + ) + session.run("flake8", "google", "tests") + + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def blacken(session): + """Run black. Format code to uniform standard.""" + session.install(BLACK_VERSION) + session.run( + "black", + *LINT_PATHS, + ) + + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def format(session): + """ + Run isort to sort imports. Then run black + to format code to uniform standard. + """ + session.install(BLACK_VERSION, ISORT_VERSION) + # Use the --fss option to sort imports using strict alphabetical order. + # See https://pycqa.github.io/isort/docs/configuration/options.html#force-sort-within-sections + session.run( + "isort", + "--fss", + *LINT_PATHS, + ) + session.run( + "black", + *LINT_PATHS, + ) + + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def lint_setup_py(session): + """Verify that setup.py is valid (including RST check).""" + session.install("setuptools", "docutils", "pygments") + session.run("python", "setup.py", "check", "--restructuredtext", "--strict") + + +def install_unittest_dependencies(session, *constraints): + standard_deps = UNIT_TEST_STANDARD_DEPENDENCIES + UNIT_TEST_DEPENDENCIES + session.install(*standard_deps, *constraints) + + if UNIT_TEST_EXTERNAL_DEPENDENCIES: + warnings.warn( + "'unit_test_external_dependencies' is deprecated. Instead, please " + "use 'unit_test_dependencies' or 'unit_test_local_dependencies'.", + DeprecationWarning, + ) + session.install(*UNIT_TEST_EXTERNAL_DEPENDENCIES, *constraints) + + if UNIT_TEST_LOCAL_DEPENDENCIES: + session.install(*UNIT_TEST_LOCAL_DEPENDENCIES, *constraints) + + if UNIT_TEST_EXTRAS_BY_PYTHON: + extras = UNIT_TEST_EXTRAS_BY_PYTHON.get(session.python, []) + elif UNIT_TEST_EXTRAS: + extras = UNIT_TEST_EXTRAS + else: + extras = [] + + if extras: + session.install("-e", f".[{','.join(extras)}]", *constraints) + else: + session.install("-e", ".", *constraints) + + +@nox.session(python=UNIT_TEST_PYTHON_VERSIONS) +@nox.parametrize( + "protobuf_implementation", + ["python", "upb", "cpp"], +) +def unit(session, protobuf_implementation): + # Install all test dependencies, then install this package in-place. + + if protobuf_implementation == "cpp" and session.python in ( + "3.11", + "3.12", + "3.13", + "3.14", + ): + session.skip("cpp implementation is not supported in python 3.11+") + + constraints_path = str( + CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" + ) + install_unittest_dependencies(session, "-c", constraints_path) + + # TODO(https://github.com/googleapis/synthtool/issues/1976): + # Remove the 'cpp' implementation once support for Protobuf 3.x is dropped. + # The 'cpp' implementation requires Protobuf<4. + if protobuf_implementation == "cpp": + session.install("protobuf<4") + + # Run py.test against the unit tests. + session.run( + "py.test", + "--quiet", + f"--junitxml=unit_{session.python}_sponge_log.xml", + "--cov=google/cloud", + "--cov=tests/unit", + "--cov-append", + "--cov-config=.coveragerc", + "--cov-report=", + "--cov-fail-under=0", + os.path.join("tests", "unit"), + *session.posargs, + env={ + "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation, + }, + ) + + +def install_systemtest_dependencies(session, *constraints): + # Use pre-release gRPC for system tests. + # Exclude version 1.52.0rc1 which has a known issue. + # See https://github.com/grpc/grpc/issues/32163 + session.install("--pre", "grpcio!=1.52.0rc1") + + session.install(*SYSTEM_TEST_STANDARD_DEPENDENCIES, *constraints) + + if SYSTEM_TEST_EXTERNAL_DEPENDENCIES: + session.install(*SYSTEM_TEST_EXTERNAL_DEPENDENCIES, *constraints) + + if SYSTEM_TEST_LOCAL_DEPENDENCIES: + session.install("-e", *SYSTEM_TEST_LOCAL_DEPENDENCIES, *constraints) + + if SYSTEM_TEST_DEPENDENCIES: + session.install("-e", *SYSTEM_TEST_DEPENDENCIES, *constraints) + + if SYSTEM_TEST_EXTRAS_BY_PYTHON: + extras = SYSTEM_TEST_EXTRAS_BY_PYTHON.get(session.python, []) + elif SYSTEM_TEST_EXTRAS: + extras = SYSTEM_TEST_EXTRAS + else: + extras = [] + + if extras: + session.install("-e", f".[{','.join(extras)}]", *constraints) + else: + session.install("-e", ".", *constraints) + + +@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS) +def system(session): + """Run the system test suite.""" + constraints_path = str( + CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" + ) + system_test_path = os.path.join("tests", "system.py") + system_test_folder_path = os.path.join("tests", "system") + + # Check the value of `RUN_SYSTEM_TESTS` env var. It defaults to true. + if os.environ.get("RUN_SYSTEM_TESTS", "true") == "false": + session.skip("RUN_SYSTEM_TESTS is set to false, skipping") + # Install pyopenssl for mTLS testing. + if os.environ.get("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") == "true": + session.install("pyopenssl") + + system_test_exists = os.path.exists(system_test_path) + system_test_folder_exists = os.path.exists(system_test_folder_path) + # Sanity check: only run tests if found. + if not system_test_exists and not system_test_folder_exists: + session.skip("System tests were not found") + + install_systemtest_dependencies(session, "-c", constraints_path) + + # Run py.test against the system tests. + if system_test_exists: + session.run( + "py.test", + "--verbose", + f"--junitxml=system_{session.python}_sponge_log.xml", + system_test_path, + *session.posargs, + ) + if os.path.exists(system_test_folder_path): + session.run( + "py.test", + "--verbose", + f"--junitxml=system_{session.python}_sponge_log.xml", + system_test_folder_path, + *session.posargs, + ) + + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def cover(session): + """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=99") + + session.run("coverage", "erase") + + +# py > 3.10 not supported yet +@nox.session(python="3.10") +def docs(session): + """Build the docs for this library.""" + + session.install("-e", ".") + 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.5.0", + "alabaster", + "recommonmark", + ) + + shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) + session.run( + "sphinx-build", + "-W", # warnings as errors + "-T", # show full traceback on exception + "-N", # no colors + "-b", + "html", + "-d", + os.path.join("docs", "_build", "doctrees", ""), + os.path.join("docs", ""), + os.path.join("docs", "_build", "html", ""), + ) + + +# py > 3.10 not supported yet +@nox.session(python="3.10") +def docfx(session): + """Build the docfx yaml files for this library.""" + + session.install("-e", ".") + 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", + "gcp-sphinx-docfx-yaml", + "alabaster", + "recommonmark", + ) + + shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) + session.run( + "sphinx-build", + "-T", # show full traceback on exception + "-N", # no colors + "-D", + ( + "extensions=sphinx.ext.autodoc," + "sphinx.ext.autosummary," + "docfx_yaml.extension," + "sphinx.ext.intersphinx," + "sphinx.ext.coverage," + "sphinx.ext.napoleon," + "sphinx.ext.todo," + "sphinx.ext.viewcode," + "recommonmark" + ), + "-b", + "html", + "-d", + os.path.join("docs", "_build", "doctrees", ""), + os.path.join("docs", ""), + os.path.join("docs", "_build", "html", ""), + ) + + +@nox.session(python="3.14") +@nox.parametrize( + "protobuf_implementation", + ["python", "upb", "cpp"], +) +def prerelease_deps(session, protobuf_implementation): + """Run all tests with prerelease versions of dependencies installed.""" + + if protobuf_implementation == "cpp" and session.python in ( + "3.11", + "3.12", + "3.13", + "3.14", + ): + session.skip("cpp implementation is not supported in python 3.11+") + + # Install all dependencies + session.install("-e", ".[all, tests, tracing]") + unit_deps_all = UNIT_TEST_STANDARD_DEPENDENCIES + UNIT_TEST_EXTERNAL_DEPENDENCIES + session.install(*unit_deps_all) + system_deps_all = ( + SYSTEM_TEST_STANDARD_DEPENDENCIES + SYSTEM_TEST_EXTERNAL_DEPENDENCIES + ) + session.install(*system_deps_all) + + # Because we test minimum dependency versions on the minimum Python + # version, the first version we test with in the unit tests sessions has a + # constraints file containing all dependencies and extras. + with open( + CURRENT_DIRECTORY + / "testing" + / f"constraints-{UNIT_TEST_PYTHON_VERSIONS[0]}.txt", + encoding="utf-8", + ) as constraints_file: + constraints_text = constraints_file.read() + + # Ignore leading whitespace and comment lines. + constraints_deps = [ + match.group(1) + for match in re.finditer( + r"^\s*(\S+)(?===\S+)", constraints_text, flags=re.MULTILINE + ) + ] + + session.install(*constraints_deps) + + prerel_deps = [ + "protobuf", + # dependency of grpc + "six", + "grpc-google-iam-v1", + "googleapis-common-protos", + "grpcio", + "grpcio-status", + "google-api-core", + "google-auth", + "proto-plus", + "google-cloud-testutils", + # dependencies of google-cloud-testutils" + "click", + ] + + for dep in prerel_deps: + session.install("--pre", "--no-deps", "--upgrade", dep) + + # Remaining dependencies + other_deps = [ + "requests", + ] + session.install(*other_deps) + + # Print out prerelease package versions + session.run( + "python", "-c", "import google.protobuf; print(google.protobuf.__version__)" + ) + session.run("python", "-c", "import grpc; print(grpc.__version__)") + session.run("python", "-c", "import google.auth; print(google.auth.__version__)") + + session.run( + "py.test", + "tests/unit", + env={ + "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation, + }, + ) + + system_test_path = os.path.join("tests", "system.py") + system_test_folder_path = os.path.join("tests", "system") + + # Only run system tests if found. + if os.path.exists(system_test_path): + session.run( + "py.test", + "--verbose", + f"--junitxml=system_{session.python}_sponge_log.xml", + system_test_path, + *session.posargs, + env={ + "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation, + }, + ) + if os.path.exists(system_test_folder_path): + session.run( + "py.test", + "--verbose", + f"--junitxml=system_{session.python}_sponge_log.xml", + system_test_folder_path, + *session.posargs, + env={ + "PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION": protobuf_implementation, + }, + ) diff --git a/.librarian/generator-input/owlbot.py b/.librarian/generator-input/owlbot.py new file mode 100644 index 000000000..5e6af7955 --- /dev/null +++ b/.librarian/generator-input/owlbot.py @@ -0,0 +1,352 @@ +# Copyright 2022 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 json +from pathlib import Path +import re +import shutil +import textwrap + +import synthtool as s +import synthtool.gcp as gcp +from synthtool.languages import python + +# ---------------------------------------------------------------------------- +# Copy the generated client from the owl-bot staging directory +# ---------------------------------------------------------------------------- + +clean_up_generated_samples = True + +# Load the default version defined in .repo-metadata.json. +default_version = json.load(open(".repo-metadata.json", "rt")).get( + "default_version" +) + +for library in s.get_staging_dirs(default_version): + if clean_up_generated_samples: + shutil.rmtree("samples/generated_samples", ignore_errors=True) + clean_up_generated_samples = False + + # DEFAULT SCOPES and SERVICE_ADDRESS are being used. so let's force them in. + s.replace( + library / f"google/pubsub_{library.name}/services/*er/*client.py", + r"""DEFAULT_ENDPOINT = \"pubsub\.googleapis\.com\"""", + """ + # The scopes needed to make gRPC calls to all of the methods defined in + # this service + _DEFAULT_SCOPES = ( + 'https://www.googleapis.com/auth/cloud-platform', + 'https://www.googleapis.com/auth/pubsub', + ) + + SERVICE_ADDRESS = "pubsub.googleapis.com:443" + \"""The default address of the service.\""" + + \g<0>""", + ) + + # Modify GRPC options in transports. + count = s.replace( + [ + library / f"google/pubsub_{library.name}/services/*/transports/grpc*", + library / f"tests/unit/gapic/pubsub_{library.name}/*", + ], + "options=\[.*?\]", + """options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ("grpc.max_metadata_size", 4 * 1024 * 1024), + ("grpc.keepalive_time_ms", 30000), + ]""", + flags=re.MULTILINE | re.DOTALL, + ) + + if count < 15: + raise Exception("Expected replacements for gRPC channel options not made.") + + # If the emulator is used, force an insecure gRPC channel to avoid SSL errors. + clients_to_patch = [ + library / f"google/pubsub_{library.name}/services/publisher/client.py", + library / f"google/pubsub_{library.name}/services/subscriber/client.py", + library / f"google/pubsub_{library.name}/services/schema_service/client.py", + ] + err_msg = ( + "Expected replacements for gRPC channel to use with the emulator not made." + ) + + count = s.replace(clients_to_patch, r"import os", "import functools\n\g<0>") + + if count < len(clients_to_patch): + raise Exception(err_msg) + + count = s.replace( + clients_to_patch, + f"from \.transports\.base", + "\nimport grpc\n\g<0>", + ) + + if count < len(clients_to_patch): + raise Exception(err_msg) + + # TODO(https://github.com/googleapis/python-pubsub/issues/1349): Move the emulator + # code below to test files. + count = s.replace( + clients_to_patch, + r"# initialize with the provided callable or the passed in class", + """\g<0> + + emulator_host = os.environ.get("PUBSUB_EMULATOR_HOST") + if emulator_host: + if issubclass(transport_init, type(self)._transport_registry["grpc"]): # type: ignore + channel = grpc.insecure_channel(target=emulator_host) + else: + channel = grpc.aio.insecure_channel(target=emulator_host) + transport_init = functools.partial(transport_init, channel=channel) + + """, + ) + + if count < len(clients_to_patch): + raise Exception(err_msg) + + # Monkey patch the streaming_pull() GAPIC method to disable pre-fetching stream + # results. + s.replace( + library / f"google/pubsub_{library.name}/services/subscriber/client.py", + ( + r"# Wrap the RPC method.*\n" + r"\s+# and friendly error.*\n" + r"\s+rpc = self\._transport\._wrapped_methods\[self\._transport\.streaming_pull\]" + ), + """ + # Wrappers in api-core should not automatically pre-fetch the first + # stream result, as this breaks the stream when re-opening it. + # https://github.com/googleapis/python-pubsub/issues/93#issuecomment-630762257 + self._transport.streaming_pull._prefetch_first_result_ = False + + \g<0>""", + ) + + # Emit deprecation warning if return_immediately flag is set with synchronous pull. + s.replace( + library / f"google/pubsub_{library.name}/services/subscriber/*client.py", + r"from google.pubsub_v1 import gapic_version as package_version", + "import warnings\n\g<0>", + ) + + count = s.replace( + library / f"google/pubsub_{library.name}/services/subscriber/*client.py", + r""" + ([^\n\S]+(?:async\ )?def\ pull\(.*?->\ pubsub\.PullResponse:.*?) + ((?P[^\n\S]+)\#\ Wrap\ the\ RPC\ method) + """, + textwrap.dedent( + """ + \g<1> + \gif request.return_immediately: + \g warnings.warn( + \g "The return_immediately flag is deprecated and should be set to False.", + \g category=DeprecationWarning, + \g ) + + \g<2>""" + ), + flags=re.MULTILINE | re.DOTALL | re.VERBOSE, + ) + + if count != 2: + raise Exception("Too many or too few replacements in pull() methods.") + + # Silence deprecation warnings in pull() method flattened parameter tests. + s.replace( + library / f"tests/unit/gapic/pubsub_{library.name}/test_subscriber.py", + "import os", + "\g<0>\nimport warnings", + ) + + count = s.replace( + library / f"tests/unit/gapic/pubsub_{library.name}/test_subscriber.py", + textwrap.dedent( + r""" + ([^\n\S]+# Call the method with a truthy value for each flattened field, + [^\n\S]+# using the keyword arguments to the method\.) + \s+(client\.pull\(.*?\))""" + ), + """\n\g<1> + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=DeprecationWarning) + \g<2>""", + flags=re.MULTILINE | re.DOTALL, + ) + + if count < 1: + raise Exception("Catch warnings replacement failed.") + + count = s.replace( + library / f"tests/unit/gapic/pubsub_{library.name}/test_subscriber.py", + textwrap.dedent( + r""" + ([^\n\S]+# Call the method with a truthy value for each flattened field, + [^\n\S]+# using the keyword arguments to the method\.) + \s+response = (await client\.pull\(.*?\))""" + ), + """\n\g<1> + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=DeprecationWarning) + \g<2>""", + flags=re.MULTILINE | re.DOTALL, + ) + + if count < 1: + raise Exception("Catch warnings replacement failed.") + + # Make sure that client library version is present in user agent header. + count = s.replace( + [ + library + / f"google/pubsub_{library.name}/services/publisher/async_client.py", + library / f"google/pubsub_{library.name}/services/publisher/client.py", + library + / f"google/pubsub_{library.name}/services/publisher/transports/base.py", + library + / f"google/pubsub_{library.name}/services/schema_service/async_client.py", + library / f"google/pubsub_{library.name}/services/schema_service/client.py", + library + / f"google/pubsub_{library.name}/services/schema_service/transports/base.py", + library + / f"google/pubsub_{library.name}/services/subscriber/async_client.py", + library / f"google/pubsub_{library.name}/services/subscriber/client.py", + library + / f"google/pubsub_{library.name}/services/subscriber/transports/base.py", + ], + r"""gapic_version=package_version.__version__""", + "client_library_version=package_version.__version__", + ) + + if count < 1: + raise Exception("client_library_version replacement failed.") + + # Allow timeout to be an instance of google.api_core.timeout.* + count = s.replace( + library / f"google/pubsub_{library.name}/types/__init__.py", + r"from \.pubsub import \(", + "from typing import Union\n\n\g<0>", + ) + + if count < 1: + raise Exception("Catch timeout replacement 1 failed.") + + count = s.replace( + library / f"google/pubsub_{library.name}/types/__init__.py", + r"__all__ = \(\n", + textwrap.dedent( + '''\ + TimeoutType = Union[ + int, + float, + "google.api_core.timeout.ConstantTimeout", + "google.api_core.timeout.ExponentialTimeout", + ] + """The type of the timeout parameter of publisher client methods.""" + + \g<0> "TimeoutType",''' + ), + ) + + if count < 1: + raise Exception("Catch timeout replacement 2 failed.") + + count = s.replace( + library / f"google/pubsub_{library.name}/services/publisher/*client.py", + r"from google.api_core import retry as retries.*\n", + "\g<0>from google.api_core import timeout as timeouts # type: ignore\n", + ) + + if count < 1: + raise Exception("Catch timeout replacement 3 failed.") + + count = s.replace( + library / f"google/pubsub_{library.name}/services/publisher/*client.py", + f"from google\.pubsub_{library.name}\.types import pubsub", + f"\g<0>\nfrom google.pubsub_{library.name}.types import TimeoutType", + ) + + if count < 1: + raise Exception("Catch timeout replacement 4 failed.") + + count = s.replace( + library / f"google/pubsub_{library.name}/services/publisher/*client.py", + r"(\s+)timeout: Union\[float, object\] = gapic_v1.method.DEFAULT.*\n", + f"\g<1>timeout: TimeoutType = gapic_{library.name}.method.DEFAULT,", + ) + + if count < 1: + raise Exception("Catch timeout replacement 5 failed.") + + count = s.replace( + library / f"google/pubsub_{library.name}/services/publisher/*client.py", + r"([^\S\r\n]+)timeout \(float\): (.*)\n", + ("\g<1>timeout (TimeoutType):\n" "\g<1> \g<2>\n"), + ) + + if count < 1: + raise Exception("Catch timeout replacement 6 failed.") + + # Override the default max retry deadline for publisher methods. + count = s.replace( + library / f"google/pubsub_{library.name}/services/publisher/transports/base.py", + r"deadline=60\.0", + "deadline=600.0", + ) + if count < 9: + raise Exception( + "Default retry deadline not overriden for all publisher methods." + ) + + # The namespace package declaration in google/cloud/__init__.py should be excluded + # from coverage. + count = s.replace( + library / ".coveragerc", + "google/pubsub/__init__.py", + """google/cloud/__init__.py + google/pubsub/__init__.py""", + ) + + if count < 1: + raise Exception(".coveragerc replacement failed.") + + s.move([library], excludes=["noxfile.py", "README.rst", "docs/**/*", "setup.py", "testing/constraints-3.7.txt", "testing/constraints-3.8.txt"]) +s.remove_staging_dirs() + +# ---------------------------------------------------------------------------- +# Add templated files +# ---------------------------------------------------------------------------- + +templated_files = gcp.CommonTemplates().py_library( + microgenerator=True, + samples=True, + cov_level=99, + versions=gcp.common.detect_versions(path="./google", default_first=True), + unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"], + unit_test_dependencies=["flaky"], + system_test_python_versions=["3.12"], + system_test_external_dependencies=["psutil","flaky"], +) +s.move(templated_files, excludes=[".coveragerc", ".github/**", "README.rst", "docs/**", ".kokoro/**"]) + +python.py_samples(skip_readmes=True) + +# run format session for all directories which have a noxfile +for noxfile in Path(".").glob("**/noxfile.py"): + s.shell.run(["nox", "-s", "blacken"], cwd=noxfile.parent, hide_output=False) diff --git a/.librarian/generator-input/setup.py b/.librarian/generator-input/setup.py new file mode 100644 index 000000000..6dbea105a --- /dev/null +++ b/.librarian/generator-input/setup.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 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 io +import os + +import setuptools # type: ignore + +package_root = os.path.abspath(os.path.dirname(__file__)) + +name = "google-cloud-pubsub" + + +description = "Google Cloud Pub/Sub API client library" + +version = {} +with open(os.path.join(package_root, "google/pubsub/gapic_version.py")) as fp: + exec(fp.read(), version) +version = version["__version__"] + +if version[0] == "0": + release_status = "Development Status :: 4 - Beta" +else: + release_status = "Development Status :: 5 - Production/Stable" + +dependencies = [ + "grpcio >= 1.51.3, < 2.0.0; python_version < '3.14'", # https://github.com/googleapis/python-pubsub/issues/609 + "grpcio >= 1.75.1, < 2.0.0; python_version >= '3.14'", + # google-api-core >= 1.34.0 is allowed in order to support google-api-core 1.x + "google-auth >= 2.14.1, <3.0.0", + "google-api-core[grpc] >= 1.34.0, <3.0.0,!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*,!=2.5.*,!=2.6.*,!=2.7.*,!=2.8.*,!=2.9.*,!=2.10.*", + "proto-plus >= 1.22.0, <2.0.0", + "proto-plus >= 1.22.2, <2.0.0; python_version>='3.11'", + "proto-plus >= 1.25.0, < 2.0.0; python_version >= '3.13'", + "protobuf>=3.20.2,<7.0.0,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5", + "grpc-google-iam-v1 >= 0.12.4, < 1.0.0", + "grpcio-status >= 1.33.2", + "opentelemetry-api <= 1.22.0; python_version<='3.7'", + "opentelemetry-api >= 1.27.0; python_version>='3.8'", + "opentelemetry-sdk <= 1.22.0; python_version<='3.7'", + "opentelemetry-sdk >= 1.27.0; python_version>='3.8'", +] +extras = {"libcst": "libcst >= 0.3.10"} +url = "https://github.com/googleapis/python-pubsub" + +package_root = os.path.abspath(os.path.dirname(__file__)) + +readme_filename = os.path.join(package_root, "README.rst") +with io.open(readme_filename, encoding="utf-8") as readme_file: + readme = readme_file.read() + +packages = [ + package + for package in setuptools.find_namespace_packages() + if package.startswith("google") +] + +setuptools.setup( + name=name, + version=version, + description=description, + long_description=readme, + author="Google LLC", + author_email="googleapis-packages@google.com", + license="Apache 2.0", + url=url, + classifiers=[ + release_status, + "Intended Audience :: Developers", + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Operating System :: OS Independent", + "Topic :: Internet", + ], + platforms="Posix; MacOS X; Windows", + packages=packages, + install_requires=dependencies, + extras_require=extras, + python_requires=">=3.7", + scripts=["scripts/fixup_pubsub_v1_keywords.py"], + include_package_data=True, + zip_safe=False, +) diff --git a/.librarian/state.yaml b/.librarian/state.yaml index 4d7702c55..bfadaea65 100644 --- a/.librarian/state.yaml +++ b/.librarian/state.yaml @@ -1,10 +1,16 @@ image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator:latest libraries: - id: google-cloud-pubsub - version: 2.32.0 - apis: [] + version: 2.33.0 + last_generated_commit: fee5b32df810adbd07d6a20bd97d9239937ef6e4 + apis: + - path: google/pubsub/v1 + service_config: pubsub_v1.yaml source_roots: - . preserve_regex: [] - remove_regex: [] + remove_regex: + - google/pubsub + - google/pubsub_v1 + - tests/unit/gapic tag_format: v{version} diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 0c5de5c03..1eb64a9d6 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2022 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 0c5de5c03..1eb64a9d6 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2022 Google LLC +# Copyright 2025 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/noxfile.py b/noxfile.py index 7455daf83..fd552166c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -30,11 +30,11 @@ FLAKE8_VERSION = "flake8==6.1.0" BLACK_VERSION = "black[jupyter]==23.7.0" ISORT_VERSION = "isort==5.11.0" -LINT_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] +LINT_PATHS = ["google", "tests", "noxfile.py", "setup.py"] MYPY_VERSION = "mypy==1.10.0" -DEFAULT_PYTHON_VERSION = "3.13" +DEFAULT_PYTHON_VERSION = "3.14" UNIT_TEST_PYTHON_VERSIONS: List[str] = [ "3.7", diff --git a/owlbot.py b/owlbot.py index abaf534e2..5e6af7955 100644 --- a/owlbot.py +++ b/owlbot.py @@ -326,7 +326,7 @@ if count < 1: raise Exception(".coveragerc replacement failed.") - s.move([library], excludes=["**/gapic_version.py", "noxfile.py", "README.rst", "docs/**/*", "setup.py", "testing/constraints-3.7.txt", "testing/constraints-3.8.txt"]) + s.move([library], excludes=["noxfile.py", "README.rst", "docs/**/*", "setup.py", "testing/constraints-3.7.txt", "testing/constraints-3.8.txt"]) s.remove_staging_dirs() # ---------------------------------------------------------------------------- diff --git a/testing/constraints-3.14.txt b/testing/constraints-3.14.txt index 1dba0484d..2ae5a677e 100644 --- a/testing/constraints-3.14.txt +++ b/testing/constraints-3.14.txt @@ -7,7 +7,7 @@ # Then this file should have google-cloud-foo>=1 google-api-core>=2 google-auth>=2 +grpcio>=1 proto-plus>=1 protobuf>=6 grpc-google-iam-v1>=0 -grpcio >= 1.75.1 From 54e4a4615c882e313cf314ad076f16911100428b Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Thu, 6 Nov 2025 15:59:02 -0500 Subject: [PATCH 179/190] chore(librarian): specify all generated files in remove_regex (#1556) This PR resolves the following issue when running `librarian generate`. Librarian now prevents generated files from clobbering existing files. All generated files must match `remove_regex` otherwise we will see errotr like `file existed in destination` ``` time=2025-11-06T20:18:57.185Z level=INFO msg="=== Docker end =================================================================" time=2025-11-06T20:18:57.185Z level=INFO msg="cleaning directories" "source roots"=[.] time=2025-11-06T20:18:57.200Z level=INFO msg="copying library files" id=google-cloud-pubsub destination=/usr/local/google/home/partheniou/git/python-pubsub source=/tmp/librarian-2318048050/output/google-cloud-pubsub time=2025-11-06T20:18:57.201Z level=ERROR msg="failed to generate library" id=google-cloud-pubsub err="file existed in destination: /usr/local/google/home/partheniou/git/python-pubsub/.coveragerc" time=2025-11-06T20:18:57.201Z level=INFO msg="generation statistics" all=1 successes=0 skipped=0 failures=1 time=2025-11-06T20:18:57.201Z level=ERROR msg="librarian command failed" err="all 1 libraries failed to generate (skipped: 0)" ``` Command used ``` librarian generate --generate-unchanged ``` --- .librarian/state.yaml | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/.librarian/state.yaml b/.librarian/state.yaml index bfadaea65..c7c871274 100644 --- a/.librarian/state.yaml +++ b/.librarian/state.yaml @@ -2,7 +2,7 @@ image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-li libraries: - id: google-cloud-pubsub version: 2.33.0 - last_generated_commit: fee5b32df810adbd07d6a20bd97d9239937ef6e4 + last_generated_commit: 9fcfbea0aa5b50fa22e190faceb073d74504172b apis: - path: google/pubsub/v1 service_config: pubsub_v1.yaml @@ -10,7 +10,28 @@ libraries: - . preserve_regex: [] remove_regex: - - google/pubsub - - google/pubsub_v1 - - tests/unit/gapic + - ^google/pubsub + - ^google/pubsub_v1 + - ^tests/unit/gapic + - ^tests/__init__.py + - ^tests/unit/__init__.py + - ^.coveragerc + - ^.flake8 + - ^.pre-commit-config.yaml + - ^.repo-metadata.json + - ^.trampolinerc + - ^LICENSE + - ^MANIFEST.in + - ^SECURITY.md + - ^mypy.ini + - ^noxfile.py + - ^owlbot.py + - ^renovate.json + - ^samples/AUTHORING_GUIDE.md + - ^samples/CONTRIBUTING.md + - ^samples/generated_samples + - ^scripts/fixup_pubsub_v1_keywords.py + - ^setup.py + - ^testing/constraints-3.9 + - ^testing/constraints-3.1 tag_format: v{version} From bea7f781d4c2e44da40ebe1e44f14559458de375 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Wed, 12 Nov 2025 11:47:45 -0800 Subject: [PATCH 180/190] chore: update container sha for librarian (#1557) This PR updates the librarian container sha for v1.0.0 support. --------- Co-authored-by: Anthonios Partheniou --- .librarian/state.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.librarian/state.yaml b/.librarian/state.yaml index c7c871274..98b1fe52d 100644 --- a/.librarian/state.yaml +++ b/.librarian/state.yaml @@ -1,4 +1,4 @@ -image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator:latest +image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:ce48ed695c727f7e13efd1fd68f466a55a0d772c87b69158720cec39965bc8b2 libraries: - id: google-cloud-pubsub version: 2.33.0 From 24761a2fedeb17f5af98a72a62306ad59306a553 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Tue, 16 Dec 2025 16:28:10 -0500 Subject: [PATCH 181/190] feat(gapic): support mTLS certificates when available (#1566) chore: librarian update image pull request: 20251216T203515Z --- .../{owlbot.py => librarian.py} | 0 .librarian/generator-input/setup.py | 1 - .librarian/state.yaml | 2 +- google/pubsub_v1/__init__.py | 104 +++++++ google/pubsub_v1/services/publisher/client.py | 46 +++- .../services/schema_service/client.py | 46 +++- .../pubsub_v1/services/subscriber/client.py | 46 +++- owlbot.py => librarian.py | 0 noxfile.py | 4 + pytest.ini | 9 + scripts/fixup_pubsub_v1_keywords.py | 210 --------------- setup.py | 5 +- tests/unit/gapic/pubsub_v1/test_publisher.py | 255 ++++++++++++++++-- .../gapic/pubsub_v1/test_schema_service.py | 255 ++++++++++++++++-- tests/unit/gapic/pubsub_v1/test_subscriber.py | 255 ++++++++++++++++-- 15 files changed, 896 insertions(+), 342 deletions(-) rename .librarian/generator-input/{owlbot.py => librarian.py} (100%) rename owlbot.py => librarian.py (100%) delete mode 100644 scripts/fixup_pubsub_v1_keywords.py diff --git a/.librarian/generator-input/owlbot.py b/.librarian/generator-input/librarian.py similarity index 100% rename from .librarian/generator-input/owlbot.py rename to .librarian/generator-input/librarian.py diff --git a/.librarian/generator-input/setup.py b/.librarian/generator-input/setup.py index 6dbea105a..dd2809f82 100644 --- a/.librarian/generator-input/setup.py +++ b/.librarian/generator-input/setup.py @@ -98,7 +98,6 @@ install_requires=dependencies, extras_require=extras, python_requires=">=3.7", - scripts=["scripts/fixup_pubsub_v1_keywords.py"], include_package_data=True, zip_safe=False, ) diff --git a/.librarian/state.yaml b/.librarian/state.yaml index 98b1fe52d..9a7ad85b3 100644 --- a/.librarian/state.yaml +++ b/.librarian/state.yaml @@ -1,4 +1,4 @@ -image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:ce48ed695c727f7e13efd1fd68f466a55a0d772c87b69158720cec39965bc8b2 +image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209 libraries: - id: google-cloud-pubsub version: 2.33.0 diff --git a/google/pubsub_v1/__init__.py b/google/pubsub_v1/__init__.py index 9e23f583c..00d6b495e 100644 --- a/google/pubsub_v1/__init__.py +++ b/google/pubsub_v1/__init__.py @@ -15,8 +15,18 @@ # from google.pubsub_v1 import gapic_version as package_version +import google.api_core as api_core +import sys + __version__ = package_version.__version__ +if sys.version_info >= (3, 8): # pragma: NO COVER + from importlib import metadata +else: # pragma: NO COVER + # TODO(https://github.com/googleapis/python-api-core/issues/835): Remove + # this code path once we drop support for Python 3.7 + import importlib_metadata as metadata + from .services.publisher import PublisherClient from .services.publisher import PublisherAsyncClient @@ -94,6 +104,100 @@ from .types.schema import Encoding from .types.schema import SchemaView +if hasattr(api_core, "check_python_version") and hasattr( + api_core, "check_dependency_versions" +): # pragma: NO COVER + api_core.check_python_version("google.pubsub_v1") # type: ignore + api_core.check_dependency_versions("google.pubsub_v1") # type: ignore +else: # pragma: NO COVER + # An older version of api_core is installed which does not define the + # functions above. We do equivalent checks manually. + try: + import warnings + import sys + + _py_version_str = sys.version.split()[0] + _package_label = "google.pubsub_v1" + if sys.version_info < (3, 9): + warnings.warn( + "You are using a non-supported Python version " + + f"({_py_version_str}). Google will not post any further " + + f"updates to {_package_label} supporting this Python version. " + + "Please upgrade to the latest Python version, or at " + + f"least to Python 3.9, and then update {_package_label}.", + FutureWarning, + ) + if sys.version_info[:2] == (3, 9): + warnings.warn( + f"You are using a Python version ({_py_version_str}) " + + f"which Google will stop supporting in {_package_label} in " + + "January 2026. Please " + + "upgrade to the latest Python version, or at " + + "least to Python 3.10, before then, and " + + f"then update {_package_label}.", + FutureWarning, + ) + + def parse_version_to_tuple(version_string: str): + """Safely converts a semantic version string to a comparable tuple of integers. + Example: "4.25.8" -> (4, 25, 8) + Ignores non-numeric parts and handles common version formats. + Args: + version_string: Version string in the format "x.y.z" or "x.y.z" + Returns: + Tuple of integers for the parsed version string. + """ + parts = [] + for part in version_string.split("."): + try: + parts.append(int(part)) + except ValueError: + # If it's a non-numeric part (e.g., '1.0.0b1' -> 'b1'), stop here. + # This is a simplification compared to 'packaging.parse_version', but sufficient + # for comparing strictly numeric semantic versions. + break + return tuple(parts) + + def _get_version(dependency_name): + try: + version_string: str = metadata.version(dependency_name) + parsed_version = parse_version_to_tuple(version_string) + return (parsed_version, version_string) + except Exception: + # Catch exceptions from metadata.version() (e.g., PackageNotFoundError) + # or errors during parse_version_to_tuple + return (None, "--") + + _dependency_package = "google.protobuf" + _next_supported_version = "4.25.8" + _next_supported_version_tuple = (4, 25, 8) + _recommendation = " (we recommend 6.x)" + (_version_used, _version_used_string) = _get_version(_dependency_package) + if _version_used and _version_used < _next_supported_version_tuple: + warnings.warn( + f"Package {_package_label} depends on " + + f"{_dependency_package}, currently installed at version " + + f"{_version_used_string}. Future updates to " + + f"{_package_label} will require {_dependency_package} at " + + f"version {_next_supported_version} or higher{_recommendation}." + + " Please ensure " + + "that either (a) your Python environment doesn't pin the " + + f"version of {_dependency_package}, so that updates to " + + f"{_package_label} can require the higher version, or " + + "(b) you manually update your Python environment to use at " + + f"least version {_next_supported_version} of " + + f"{_dependency_package}.", + FutureWarning, + ) + except Exception: + warnings.warn( + "Could not determine the version of Python " + + "currently being used. To continue receiving " + + "updates for {_package_label}, ensure you are " + + "using a supported version of Python; see " + + "https://devguide.python.org/versions/" + ) + __all__ = ( "PublisherAsyncClient", "SchemaServiceAsyncClient", diff --git a/google/pubsub_v1/services/publisher/client.py b/google/pubsub_v1/services/publisher/client.py index 6debee7ee..27ed4dce8 100644 --- a/google/pubsub_v1/services/publisher/client.py +++ b/google/pubsub_v1/services/publisher/client.py @@ -168,6 +168,34 @@ def _get_default_mtls_endpoint(api_endpoint): _DEFAULT_ENDPOINT_TEMPLATE = "pubsub.{UNIVERSE_DOMAIN}" _DEFAULT_UNIVERSE = "googleapis.com" + @staticmethod + def _use_client_cert_effective(): + """Returns whether client certificate should be used for mTLS if the + google-auth version supports should_use_client_cert automatic mTLS enablement. + + Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var. + + Returns: + bool: whether client certificate should be used for mTLS + Raises: + ValueError: (If using a version of google-auth without should_use_client_cert and + GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.) + """ + # check if google-auth version supports should_use_client_cert for automatic mTLS enablement + if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER + return mtls.should_use_client_cert() + else: # pragma: NO COVER + # if unsupported, fallback to reading from env var + use_client_cert_str = os.getenv( + "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false" + ).lower() + if use_client_cert_str not in ("true", "false"): + raise ValueError( + "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be" + " either `true` or `false`" + ) + return use_client_cert_str == "true" + @classmethod def from_service_account_info(cls, info: dict, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -427,12 +455,8 @@ def get_mtls_endpoint_and_cert_source( ) if client_options is None: client_options = client_options_lib.ClientOptions() - use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") + use_client_cert = PublisherClient._use_client_cert_effective() use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") - if use_client_cert not in ("true", "false"): - raise ValueError( - "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" - ) if use_mtls_endpoint not in ("auto", "never", "always"): raise MutualTLSChannelError( "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" @@ -440,7 +464,7 @@ def get_mtls_endpoint_and_cert_source( # Figure out the client cert source to use. client_cert_source = None - if use_client_cert == "true": + if use_client_cert: if client_options.client_cert_source: client_cert_source = client_options.client_cert_source elif mtls.has_default_client_cert_source(): @@ -472,20 +496,14 @@ def _read_environment_variables(): google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT is not any of ["auto", "never", "always"]. """ - use_client_cert = os.getenv( - "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false" - ).lower() + use_client_cert = PublisherClient._use_client_cert_effective() use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower() universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN") - if use_client_cert not in ("true", "false"): - raise ValueError( - "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" - ) if use_mtls_endpoint not in ("auto", "never", "always"): raise MutualTLSChannelError( "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" ) - return use_client_cert == "true", use_mtls_endpoint, universe_domain_env + return use_client_cert, use_mtls_endpoint, universe_domain_env @staticmethod def _get_client_cert_source(provided_cert_source, use_cert_flag): diff --git a/google/pubsub_v1/services/schema_service/client.py b/google/pubsub_v1/services/schema_service/client.py index 29730b85e..300f23998 100644 --- a/google/pubsub_v1/services/schema_service/client.py +++ b/google/pubsub_v1/services/schema_service/client.py @@ -153,6 +153,34 @@ def _get_default_mtls_endpoint(api_endpoint): _DEFAULT_ENDPOINT_TEMPLATE = "pubsub.{UNIVERSE_DOMAIN}" _DEFAULT_UNIVERSE = "googleapis.com" + @staticmethod + def _use_client_cert_effective(): + """Returns whether client certificate should be used for mTLS if the + google-auth version supports should_use_client_cert automatic mTLS enablement. + + Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var. + + Returns: + bool: whether client certificate should be used for mTLS + Raises: + ValueError: (If using a version of google-auth without should_use_client_cert and + GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.) + """ + # check if google-auth version supports should_use_client_cert for automatic mTLS enablement + if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER + return mtls.should_use_client_cert() + else: # pragma: NO COVER + # if unsupported, fallback to reading from env var + use_client_cert_str = os.getenv( + "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false" + ).lower() + if use_client_cert_str not in ("true", "false"): + raise ValueError( + "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be" + " either `true` or `false`" + ) + return use_client_cert_str == "true" + @classmethod def from_service_account_info(cls, info: dict, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -335,12 +363,8 @@ def get_mtls_endpoint_and_cert_source( ) if client_options is None: client_options = client_options_lib.ClientOptions() - use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") + use_client_cert = SchemaServiceClient._use_client_cert_effective() use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") - if use_client_cert not in ("true", "false"): - raise ValueError( - "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" - ) if use_mtls_endpoint not in ("auto", "never", "always"): raise MutualTLSChannelError( "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" @@ -348,7 +372,7 @@ def get_mtls_endpoint_and_cert_source( # Figure out the client cert source to use. client_cert_source = None - if use_client_cert == "true": + if use_client_cert: if client_options.client_cert_source: client_cert_source = client_options.client_cert_source elif mtls.has_default_client_cert_source(): @@ -380,20 +404,14 @@ def _read_environment_variables(): google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT is not any of ["auto", "never", "always"]. """ - use_client_cert = os.getenv( - "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false" - ).lower() + use_client_cert = SchemaServiceClient._use_client_cert_effective() use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower() universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN") - if use_client_cert not in ("true", "false"): - raise ValueError( - "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" - ) if use_mtls_endpoint not in ("auto", "never", "always"): raise MutualTLSChannelError( "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" ) - return use_client_cert == "true", use_mtls_endpoint, universe_domain_env + return use_client_cert, use_mtls_endpoint, universe_domain_env @staticmethod def _get_client_cert_source(provided_cert_source, use_cert_flag): diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index 98b11e8c3..2a946f726 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -172,6 +172,34 @@ def _get_default_mtls_endpoint(api_endpoint): _DEFAULT_ENDPOINT_TEMPLATE = "pubsub.{UNIVERSE_DOMAIN}" _DEFAULT_UNIVERSE = "googleapis.com" + @staticmethod + def _use_client_cert_effective(): + """Returns whether client certificate should be used for mTLS if the + google-auth version supports should_use_client_cert automatic mTLS enablement. + + Alternatively, read from the GOOGLE_API_USE_CLIENT_CERTIFICATE env var. + + Returns: + bool: whether client certificate should be used for mTLS + Raises: + ValueError: (If using a version of google-auth without should_use_client_cert and + GOOGLE_API_USE_CLIENT_CERTIFICATE is set to an unexpected value.) + """ + # check if google-auth version supports should_use_client_cert for automatic mTLS enablement + if hasattr(mtls, "should_use_client_cert"): # pragma: NO COVER + return mtls.should_use_client_cert() + else: # pragma: NO COVER + # if unsupported, fallback to reading from env var + use_client_cert_str = os.getenv( + "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false" + ).lower() + if use_client_cert_str not in ("true", "false"): + raise ValueError( + "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be" + " either `true` or `false`" + ) + return use_client_cert_str == "true" + @classmethod def from_service_account_info(cls, info: dict, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -414,12 +442,8 @@ def get_mtls_endpoint_and_cert_source( ) if client_options is None: client_options = client_options_lib.ClientOptions() - use_client_cert = os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false") + use_client_cert = SubscriberClient._use_client_cert_effective() use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") - if use_client_cert not in ("true", "false"): - raise ValueError( - "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" - ) if use_mtls_endpoint not in ("auto", "never", "always"): raise MutualTLSChannelError( "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" @@ -427,7 +451,7 @@ def get_mtls_endpoint_and_cert_source( # Figure out the client cert source to use. client_cert_source = None - if use_client_cert == "true": + if use_client_cert: if client_options.client_cert_source: client_cert_source = client_options.client_cert_source elif mtls.has_default_client_cert_source(): @@ -459,20 +483,14 @@ def _read_environment_variables(): google.auth.exceptions.MutualTLSChannelError: If GOOGLE_API_USE_MTLS_ENDPOINT is not any of ["auto", "never", "always"]. """ - use_client_cert = os.getenv( - "GOOGLE_API_USE_CLIENT_CERTIFICATE", "false" - ).lower() + use_client_cert = SubscriberClient._use_client_cert_effective() use_mtls_endpoint = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto").lower() universe_domain_env = os.getenv("GOOGLE_CLOUD_UNIVERSE_DOMAIN") - if use_client_cert not in ("true", "false"): - raise ValueError( - "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" - ) if use_mtls_endpoint not in ("auto", "never", "always"): raise MutualTLSChannelError( "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" ) - return use_client_cert == "true", use_mtls_endpoint, universe_domain_env + return use_client_cert, use_mtls_endpoint, universe_domain_env @staticmethod def _get_client_cert_source(provided_cert_source, use_cert_flag): diff --git a/owlbot.py b/librarian.py similarity index 100% rename from owlbot.py rename to librarian.py diff --git a/noxfile.py b/noxfile.py index fd552166c..d1b3c15d1 100644 --- a/noxfile.py +++ b/noxfile.py @@ -14,6 +14,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +# DO NOT EDIT THIS FILE OUTSIDE OF `.librarian/generator-input` +# The source of truth for this file is `.librarian/generator-input` + + # Generated by synthtool. DO NOT EDIT! from __future__ import absolute_import diff --git a/pytest.ini b/pytest.ini index 165f566b6..a1f4f115e 100644 --- a/pytest.ini +++ b/pytest.ini @@ -27,3 +27,12 @@ filterwarnings = ignore:You are using a Python version.*which Google will stop supporting in new releases of google\.api_core.*:FutureWarning ignore:You are using a non-supported Python version \(([\d\.]+)\)\. Google will not post any further updates to google\.api_core.*:FutureWarning ignore:You are using a Python version \(([\d\.]+)\) past its end of life\. Google will update google\.api_core.*:FutureWarning + # Remove after support for Python 3.7 is dropped + ignore:You are using a non-supported Python version \(3\.7:FutureWarning + # Remove after support for Python 3.8 is dropped + ignore:You are using a non-supported Python version \(3\.8:DeprecationWarning + ignore:You are using a non-supported Python version \(3\.8:FutureWarning + # Remove after support for Python 3.9 is dropped + ignore:You are using a Python version \(3\.9:FutureWarning + # Remove after support for Python 3.10 is dropped + ignore:.*You are using a Python version \(3\.10:FutureWarning diff --git a/scripts/fixup_pubsub_v1_keywords.py b/scripts/fixup_pubsub_v1_keywords.py deleted file mode 100644 index e4c132570..000000000 --- a/scripts/fixup_pubsub_v1_keywords.py +++ /dev/null @@ -1,210 +0,0 @@ -#! /usr/bin/env python3 -# -*- coding: utf-8 -*- -# Copyright 2025 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 argparse -import os -import libcst as cst -import pathlib -import sys -from typing import (Any, Callable, Dict, List, Sequence, Tuple) - - -def partition( - predicate: Callable[[Any], bool], - iterator: Sequence[Any] -) -> Tuple[List[Any], List[Any]]: - """A stable, out-of-place partition.""" - results = ([], []) - - for i in iterator: - results[int(predicate(i))].append(i) - - # Returns trueList, falseList - return results[1], results[0] - - -class pubsubCallTransformer(cst.CSTTransformer): - CTRL_PARAMS: Tuple[str] = ('retry', 'timeout', 'metadata') - METHOD_TO_PARAMS: Dict[str, Tuple[str]] = { - 'acknowledge': ('subscription', 'ack_ids', ), - 'commit_schema': ('name', 'schema', ), - 'create_schema': ('parent', 'schema', 'schema_id', ), - 'create_snapshot': ('name', 'subscription', 'labels', 'tags', ), - 'create_subscription': ('name', 'topic', 'push_config', 'bigquery_config', 'cloud_storage_config', 'ack_deadline_seconds', 'retain_acked_messages', 'message_retention_duration', 'labels', 'enable_message_ordering', 'expiration_policy', 'filter', 'dead_letter_policy', 'retry_policy', 'detached', 'enable_exactly_once_delivery', 'topic_message_retention_duration', 'state', 'analytics_hub_subscription_info', 'message_transforms', 'tags', ), - 'create_topic': ('name', 'labels', 'message_storage_policy', 'kms_key_name', 'schema_settings', 'satisfies_pzs', 'message_retention_duration', 'state', 'ingestion_data_source_settings', 'message_transforms', 'tags', ), - 'delete_schema': ('name', ), - 'delete_schema_revision': ('name', 'revision_id', ), - 'delete_snapshot': ('snapshot', ), - 'delete_subscription': ('subscription', ), - 'delete_topic': ('topic', ), - 'detach_subscription': ('subscription', ), - 'get_schema': ('name', 'view', ), - 'get_snapshot': ('snapshot', ), - 'get_subscription': ('subscription', ), - 'get_topic': ('topic', ), - 'list_schema_revisions': ('name', 'view', 'page_size', 'page_token', ), - 'list_schemas': ('parent', 'view', 'page_size', 'page_token', ), - 'list_snapshots': ('project', 'page_size', 'page_token', ), - 'list_subscriptions': ('project', 'page_size', 'page_token', ), - 'list_topics': ('project', 'page_size', 'page_token', ), - 'list_topic_snapshots': ('topic', 'page_size', 'page_token', ), - 'list_topic_subscriptions': ('topic', 'page_size', 'page_token', ), - 'modify_ack_deadline': ('subscription', 'ack_ids', 'ack_deadline_seconds', ), - 'modify_push_config': ('subscription', 'push_config', ), - 'publish': ('topic', 'messages', ), - 'pull': ('subscription', 'max_messages', 'return_immediately', ), - 'rollback_schema': ('name', 'revision_id', ), - 'seek': ('subscription', 'time', 'snapshot', ), - 'streaming_pull': ('subscription', 'stream_ack_deadline_seconds', 'ack_ids', 'modify_deadline_seconds', 'modify_deadline_ack_ids', 'client_id', 'max_outstanding_messages', 'max_outstanding_bytes', 'protocol_version', ), - 'update_snapshot': ('snapshot', 'update_mask', ), - 'update_subscription': ('subscription', 'update_mask', ), - 'update_topic': ('topic', 'update_mask', ), - 'validate_message': ('parent', 'name', 'schema', 'message', 'encoding', ), - 'validate_schema': ('parent', 'schema', ), - } - - def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode: - try: - key = original.func.attr.value - kword_params = self.METHOD_TO_PARAMS[key] - except (AttributeError, KeyError): - # Either not a method from the API or too convoluted to be sure. - return updated - - # If the existing code is valid, keyword args come after positional args. - # Therefore, all positional args must map to the first parameters. - args, kwargs = partition(lambda a: not bool(a.keyword), updated.args) - if any(k.keyword.value == "request" for k in kwargs): - # We've already fixed this file, don't fix it again. - return updated - - kwargs, ctrl_kwargs = partition( - lambda a: a.keyword.value not in self.CTRL_PARAMS, - kwargs - ) - - args, ctrl_args = args[:len(kword_params)], args[len(kword_params):] - ctrl_kwargs.extend(cst.Arg(value=a.value, keyword=cst.Name(value=ctrl)) - for a, ctrl in zip(ctrl_args, self.CTRL_PARAMS)) - - request_arg = cst.Arg( - value=cst.Dict([ - cst.DictElement( - cst.SimpleString("'{}'".format(name)), -cst.Element(value=arg.value) - ) - # Note: the args + kwargs looks silly, but keep in mind that - # the control parameters had to be stripped out, and that - # those could have been passed positionally or by keyword. - for name, arg in zip(kword_params, args + kwargs)]), - keyword=cst.Name("request") - ) - - return updated.with_changes( - args=[request_arg] + ctrl_kwargs - ) - - -def fix_files( - in_dir: pathlib.Path, - out_dir: pathlib.Path, - *, - transformer=pubsubCallTransformer(), -): - """Duplicate the input dir to the output dir, fixing file method calls. - - Preconditions: - * in_dir is a real directory - * out_dir is a real, empty directory - """ - pyfile_gen = ( - pathlib.Path(os.path.join(root, f)) - for root, _, files in os.walk(in_dir) - for f in files if os.path.splitext(f)[1] == ".py" - ) - - for fpath in pyfile_gen: - with open(fpath, 'r') as f: - src = f.read() - - # Parse the code and insert method call fixes. - tree = cst.parse_module(src) - updated = tree.visit(transformer) - - # Create the path and directory structure for the new file. - updated_path = out_dir.joinpath(fpath.relative_to(in_dir)) - updated_path.parent.mkdir(parents=True, exist_ok=True) - - # Generate the updated source file at the corresponding path. - with open(updated_path, 'w') as f: - f.write(updated.code) - - -if __name__ == '__main__': - parser = argparse.ArgumentParser( - description="""Fix up source that uses the pubsub client library. - -The existing sources are NOT overwritten but are copied to output_dir with changes made. - -Note: This tool operates at a best-effort level at converting positional - parameters in client method calls to keyword based parameters. - Cases where it WILL FAIL include - A) * or ** expansion in a method call. - B) Calls via function or method alias (includes free function calls) - C) Indirect or dispatched calls (e.g. the method is looked up dynamically) - - These all constitute false negatives. The tool will also detect false - positives when an API method shares a name with another method. -""") - parser.add_argument( - '-d', - '--input-directory', - required=True, - dest='input_dir', - help='the input directory to walk for python files to fix up', - ) - parser.add_argument( - '-o', - '--output-directory', - required=True, - dest='output_dir', - help='the directory to output files fixed via un-flattening', - ) - args = parser.parse_args() - input_dir = pathlib.Path(args.input_dir) - output_dir = pathlib.Path(args.output_dir) - if not input_dir.is_dir(): - print( - f"input directory '{input_dir}' does not exist or is not a directory", - file=sys.stderr, - ) - sys.exit(-1) - - if not output_dir.is_dir(): - print( - f"output directory '{output_dir}' does not exist or is not a directory", - file=sys.stderr, - ) - sys.exit(-1) - - if os.listdir(output_dir): - print( - f"output directory '{output_dir}' is not empty", - file=sys.stderr, - ) - sys.exit(-1) - - fix_files(input_dir, output_dir) diff --git a/setup.py b/setup.py index 6dbea105a..74a11ebf6 100644 --- a/setup.py +++ b/setup.py @@ -13,6 +13,10 @@ # See the License for the specific language governing permissions and # limitations under the License. # + +# DO NOT EDIT THIS FILE OUTSIDE OF `.librarian/generator-input` +# The source of truth for this file is `.librarian/generator-input` + import io import os @@ -98,7 +102,6 @@ install_requires=dependencies, extras_require=extras, python_requires=">=3.7", - scripts=["scripts/fixup_pubsub_v1_keywords.py"], include_package_data=True, zip_safe=False, ) diff --git a/tests/unit/gapic/pubsub_v1/test_publisher.py b/tests/unit/gapic/pubsub_v1/test_publisher.py index 6748ac09a..978021fcd 100644 --- a/tests/unit/gapic/pubsub_v1/test_publisher.py +++ b/tests/unit/gapic/pubsub_v1/test_publisher.py @@ -153,12 +153,19 @@ def test__read_environment_variables(): with mock.patch.dict( os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} ): - with pytest.raises(ValueError) as excinfo: - PublisherClient._read_environment_variables() - assert ( - str(excinfo.value) - == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" - ) + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with pytest.raises(ValueError) as excinfo: + PublisherClient._read_environment_variables() + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) + else: + assert PublisherClient._read_environment_variables() == ( + False, + "auto", + None, + ) with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): assert PublisherClient._read_environment_variables() == (False, "never", None) @@ -185,6 +192,105 @@ def test__read_environment_variables(): ) +def test_use_client_cert_effective(): + # Test case 1: Test when `should_use_client_cert` returns True. + # We mock the `should_use_client_cert` function to simulate a scenario where + # the google-auth library supports automatic mTLS and determines that a + # client certificate should be used. + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch( + "google.auth.transport.mtls.should_use_client_cert", return_value=True + ): + assert PublisherClient._use_client_cert_effective() is True + + # Test case 2: Test when `should_use_client_cert` returns False. + # We mock the `should_use_client_cert` function to simulate a scenario where + # the google-auth library supports automatic mTLS and determines that a + # client certificate should NOT be used. + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch( + "google.auth.transport.mtls.should_use_client_cert", return_value=False + ): + assert PublisherClient._use_client_cert_effective() is False + + # Test case 3: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}): + assert PublisherClient._use_client_cert_effective() is True + + # Test case 4: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "false". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"} + ): + assert PublisherClient._use_client_cert_effective() is False + + # Test case 5: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "True". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "True"}): + assert PublisherClient._use_client_cert_effective() is True + + # Test case 6: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "False". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "False"} + ): + assert PublisherClient._use_client_cert_effective() is False + + # Test case 7: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "TRUE". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "TRUE"}): + assert PublisherClient._use_client_cert_effective() is True + + # Test case 8: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "FALSE". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "FALSE"} + ): + assert PublisherClient._use_client_cert_effective() is False + + # Test case 9: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not set. + # In this case, the method should return False, which is the default value. + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict(os.environ, clear=True): + assert PublisherClient._use_client_cert_effective() is False + + # Test case 10: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value. + # The method should raise a ValueError as the environment variable must be either + # "true" or "false". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"} + ): + with pytest.raises(ValueError): + PublisherClient._use_client_cert_effective() + + # Test case 11: Test when `should_use_client_cert` is available and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value. + # The method should return False as the environment variable is set to an invalid value. + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"} + ): + assert PublisherClient._use_client_cert_effective() is False + + # Test case 12: Test when `should_use_client_cert` is available and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is unset. Also, + # the GOOGLE_API_CONFIG environment variable is unset. + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": ""}): + with mock.patch.dict(os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": ""}): + assert PublisherClient._use_client_cert_effective() is False + + def test__get_client_cert_source(): mock_provided_cert_source = mock.Mock() mock_default_cert_source = mock.Mock() @@ -548,17 +654,6 @@ def test_publisher_client_client_options(client_class, transport_class, transpor == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" ) - # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} - ): - with pytest.raises(ValueError) as excinfo: - client = client_class(transport=transport_name) - assert ( - str(excinfo.value) - == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" - ) - # Check the case quota_project_id is provided options = client_options.ClientOptions(quota_project_id="octopus") with mock.patch.object(transport_class, "__init__") as patched: @@ -770,6 +865,119 @@ def test_publisher_client_get_mtls_endpoint_and_cert_source(client_class): assert api_endpoint == mock_api_endpoint assert cert_source is None + # Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "Unsupported". + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + mock_client_cert_source = mock.Mock() + mock_api_endpoint = "foo" + options = client_options.ClientOptions( + client_cert_source=mock_client_cert_source, + api_endpoint=mock_api_endpoint, + ) + api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source( + options + ) + assert api_endpoint == mock_api_endpoint + assert cert_source is None + + # Test cases for mTLS enablement when GOOGLE_API_USE_CLIENT_CERTIFICATE is unset. + test_cases = [ + ( + # With workloads present in config, mTLS is enabled. + { + "version": 1, + "cert_configs": { + "workload": { + "cert_path": "path/to/cert/file", + "key_path": "path/to/key/file", + } + }, + }, + mock_client_cert_source, + ), + ( + # With workloads not present in config, mTLS is disabled. + { + "version": 1, + "cert_configs": {}, + }, + None, + ), + ] + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + for config_data, expected_cert_source in test_cases: + env = os.environ.copy() + env.pop("GOOGLE_API_USE_CLIENT_CERTIFICATE", None) + with mock.patch.dict(os.environ, env, clear=True): + config_filename = "mock_certificate_config.json" + config_file_content = json.dumps(config_data) + m = mock.mock_open(read_data=config_file_content) + with mock.patch("builtins.open", m): + with mock.patch.dict( + os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": config_filename} + ): + mock_api_endpoint = "foo" + options = client_options.ClientOptions( + client_cert_source=mock_client_cert_source, + api_endpoint=mock_api_endpoint, + ) + ( + api_endpoint, + cert_source, + ) = client_class.get_mtls_endpoint_and_cert_source(options) + assert api_endpoint == mock_api_endpoint + assert cert_source is expected_cert_source + + # Test cases for mTLS enablement when GOOGLE_API_USE_CLIENT_CERTIFICATE is unset(empty). + test_cases = [ + ( + # With workloads present in config, mTLS is enabled. + { + "version": 1, + "cert_configs": { + "workload": { + "cert_path": "path/to/cert/file", + "key_path": "path/to/key/file", + } + }, + }, + mock_client_cert_source, + ), + ( + # With workloads not present in config, mTLS is disabled. + { + "version": 1, + "cert_configs": {}, + }, + None, + ), + ] + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + for config_data, expected_cert_source in test_cases: + env = os.environ.copy() + env.pop("GOOGLE_API_USE_CLIENT_CERTIFICATE", "") + with mock.patch.dict(os.environ, env, clear=True): + config_filename = "mock_certificate_config.json" + config_file_content = json.dumps(config_data) + m = mock.mock_open(read_data=config_file_content) + with mock.patch("builtins.open", m): + with mock.patch.dict( + os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": config_filename} + ): + mock_api_endpoint = "foo" + options = client_options.ClientOptions( + client_cert_source=mock_client_cert_source, + api_endpoint=mock_api_endpoint, + ) + ( + api_endpoint, + cert_source, + ) = client_class.get_mtls_endpoint_and_cert_source(options) + assert api_endpoint == mock_api_endpoint + assert cert_source is expected_cert_source + # Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "never". with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source() @@ -820,18 +1028,6 @@ def test_publisher_client_get_mtls_endpoint_and_cert_source(client_class): == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" ) - # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} - ): - with pytest.raises(ValueError) as excinfo: - client_class.get_mtls_endpoint_and_cert_source() - - assert ( - str(excinfo.value) - == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" - ) - @pytest.mark.parametrize("client_class", [PublisherClient, PublisherAsyncClient]) @mock.patch.object( @@ -8804,6 +9000,7 @@ def test_publisher_grpc_asyncio_transport_channel(): # Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are # removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.parametrize( "transport_class", [transports.PublisherGrpcTransport, transports.PublisherGrpcAsyncIOTransport], diff --git a/tests/unit/gapic/pubsub_v1/test_schema_service.py b/tests/unit/gapic/pubsub_v1/test_schema_service.py index 7f8355861..f71b66805 100644 --- a/tests/unit/gapic/pubsub_v1/test_schema_service.py +++ b/tests/unit/gapic/pubsub_v1/test_schema_service.py @@ -160,12 +160,19 @@ def test__read_environment_variables(): with mock.patch.dict( os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} ): - with pytest.raises(ValueError) as excinfo: - SchemaServiceClient._read_environment_variables() - assert ( - str(excinfo.value) - == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" - ) + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with pytest.raises(ValueError) as excinfo: + SchemaServiceClient._read_environment_variables() + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) + else: + assert SchemaServiceClient._read_environment_variables() == ( + False, + "auto", + None, + ) with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): assert SchemaServiceClient._read_environment_variables() == ( @@ -204,6 +211,105 @@ def test__read_environment_variables(): ) +def test_use_client_cert_effective(): + # Test case 1: Test when `should_use_client_cert` returns True. + # We mock the `should_use_client_cert` function to simulate a scenario where + # the google-auth library supports automatic mTLS and determines that a + # client certificate should be used. + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch( + "google.auth.transport.mtls.should_use_client_cert", return_value=True + ): + assert SchemaServiceClient._use_client_cert_effective() is True + + # Test case 2: Test when `should_use_client_cert` returns False. + # We mock the `should_use_client_cert` function to simulate a scenario where + # the google-auth library supports automatic mTLS and determines that a + # client certificate should NOT be used. + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch( + "google.auth.transport.mtls.should_use_client_cert", return_value=False + ): + assert SchemaServiceClient._use_client_cert_effective() is False + + # Test case 3: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}): + assert SchemaServiceClient._use_client_cert_effective() is True + + # Test case 4: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "false". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"} + ): + assert SchemaServiceClient._use_client_cert_effective() is False + + # Test case 5: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "True". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "True"}): + assert SchemaServiceClient._use_client_cert_effective() is True + + # Test case 6: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "False". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "False"} + ): + assert SchemaServiceClient._use_client_cert_effective() is False + + # Test case 7: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "TRUE". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "TRUE"}): + assert SchemaServiceClient._use_client_cert_effective() is True + + # Test case 8: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "FALSE". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "FALSE"} + ): + assert SchemaServiceClient._use_client_cert_effective() is False + + # Test case 9: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not set. + # In this case, the method should return False, which is the default value. + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict(os.environ, clear=True): + assert SchemaServiceClient._use_client_cert_effective() is False + + # Test case 10: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value. + # The method should raise a ValueError as the environment variable must be either + # "true" or "false". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"} + ): + with pytest.raises(ValueError): + SchemaServiceClient._use_client_cert_effective() + + # Test case 11: Test when `should_use_client_cert` is available and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value. + # The method should return False as the environment variable is set to an invalid value. + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"} + ): + assert SchemaServiceClient._use_client_cert_effective() is False + + # Test case 12: Test when `should_use_client_cert` is available and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is unset. Also, + # the GOOGLE_API_CONFIG environment variable is unset. + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": ""}): + with mock.patch.dict(os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": ""}): + assert SchemaServiceClient._use_client_cert_effective() is False + + def test__get_client_cert_source(): mock_provided_cert_source = mock.Mock() mock_default_cert_source = mock.Mock() @@ -569,17 +675,6 @@ def test_schema_service_client_client_options( == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" ) - # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} - ): - with pytest.raises(ValueError) as excinfo: - client = client_class(transport=transport_name) - assert ( - str(excinfo.value) - == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" - ) - # Check the case quota_project_id is provided options = client_options.ClientOptions(quota_project_id="octopus") with mock.patch.object(transport_class, "__init__") as patched: @@ -795,6 +890,119 @@ def test_schema_service_client_get_mtls_endpoint_and_cert_source(client_class): assert api_endpoint == mock_api_endpoint assert cert_source is None + # Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "Unsupported". + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + mock_client_cert_source = mock.Mock() + mock_api_endpoint = "foo" + options = client_options.ClientOptions( + client_cert_source=mock_client_cert_source, + api_endpoint=mock_api_endpoint, + ) + api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source( + options + ) + assert api_endpoint == mock_api_endpoint + assert cert_source is None + + # Test cases for mTLS enablement when GOOGLE_API_USE_CLIENT_CERTIFICATE is unset. + test_cases = [ + ( + # With workloads present in config, mTLS is enabled. + { + "version": 1, + "cert_configs": { + "workload": { + "cert_path": "path/to/cert/file", + "key_path": "path/to/key/file", + } + }, + }, + mock_client_cert_source, + ), + ( + # With workloads not present in config, mTLS is disabled. + { + "version": 1, + "cert_configs": {}, + }, + None, + ), + ] + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + for config_data, expected_cert_source in test_cases: + env = os.environ.copy() + env.pop("GOOGLE_API_USE_CLIENT_CERTIFICATE", None) + with mock.patch.dict(os.environ, env, clear=True): + config_filename = "mock_certificate_config.json" + config_file_content = json.dumps(config_data) + m = mock.mock_open(read_data=config_file_content) + with mock.patch("builtins.open", m): + with mock.patch.dict( + os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": config_filename} + ): + mock_api_endpoint = "foo" + options = client_options.ClientOptions( + client_cert_source=mock_client_cert_source, + api_endpoint=mock_api_endpoint, + ) + ( + api_endpoint, + cert_source, + ) = client_class.get_mtls_endpoint_and_cert_source(options) + assert api_endpoint == mock_api_endpoint + assert cert_source is expected_cert_source + + # Test cases for mTLS enablement when GOOGLE_API_USE_CLIENT_CERTIFICATE is unset(empty). + test_cases = [ + ( + # With workloads present in config, mTLS is enabled. + { + "version": 1, + "cert_configs": { + "workload": { + "cert_path": "path/to/cert/file", + "key_path": "path/to/key/file", + } + }, + }, + mock_client_cert_source, + ), + ( + # With workloads not present in config, mTLS is disabled. + { + "version": 1, + "cert_configs": {}, + }, + None, + ), + ] + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + for config_data, expected_cert_source in test_cases: + env = os.environ.copy() + env.pop("GOOGLE_API_USE_CLIENT_CERTIFICATE", "") + with mock.patch.dict(os.environ, env, clear=True): + config_filename = "mock_certificate_config.json" + config_file_content = json.dumps(config_data) + m = mock.mock_open(read_data=config_file_content) + with mock.patch("builtins.open", m): + with mock.patch.dict( + os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": config_filename} + ): + mock_api_endpoint = "foo" + options = client_options.ClientOptions( + client_cert_source=mock_client_cert_source, + api_endpoint=mock_api_endpoint, + ) + ( + api_endpoint, + cert_source, + ) = client_class.get_mtls_endpoint_and_cert_source(options) + assert api_endpoint == mock_api_endpoint + assert cert_source is expected_cert_source + # Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "never". with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source() @@ -845,18 +1053,6 @@ def test_schema_service_client_get_mtls_endpoint_and_cert_source(client_class): == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" ) - # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} - ): - with pytest.raises(ValueError) as excinfo: - client_class.get_mtls_endpoint_and_cert_source() - - assert ( - str(excinfo.value) - == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" - ) - @pytest.mark.parametrize( "client_class", [SchemaServiceClient, SchemaServiceAsyncClient] @@ -9437,6 +9633,7 @@ def test_schema_service_grpc_asyncio_transport_channel(): # Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are # removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.parametrize( "transport_class", [ diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py index 9186c815a..816b04500 100644 --- a/tests/unit/gapic/pubsub_v1/test_subscriber.py +++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py @@ -155,12 +155,19 @@ def test__read_environment_variables(): with mock.patch.dict( os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} ): - with pytest.raises(ValueError) as excinfo: - SubscriberClient._read_environment_variables() - assert ( - str(excinfo.value) - == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" - ) + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with pytest.raises(ValueError) as excinfo: + SubscriberClient._read_environment_variables() + assert ( + str(excinfo.value) + == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" + ) + else: + assert SubscriberClient._read_environment_variables() == ( + False, + "auto", + None, + ) with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): assert SubscriberClient._read_environment_variables() == (False, "never", None) @@ -187,6 +194,105 @@ def test__read_environment_variables(): ) +def test_use_client_cert_effective(): + # Test case 1: Test when `should_use_client_cert` returns True. + # We mock the `should_use_client_cert` function to simulate a scenario where + # the google-auth library supports automatic mTLS and determines that a + # client certificate should be used. + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch( + "google.auth.transport.mtls.should_use_client_cert", return_value=True + ): + assert SubscriberClient._use_client_cert_effective() is True + + # Test case 2: Test when `should_use_client_cert` returns False. + # We mock the `should_use_client_cert` function to simulate a scenario where + # the google-auth library supports automatic mTLS and determines that a + # client certificate should NOT be used. + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch( + "google.auth.transport.mtls.should_use_client_cert", return_value=False + ): + assert SubscriberClient._use_client_cert_effective() is False + + # Test case 3: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "true". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "true"}): + assert SubscriberClient._use_client_cert_effective() is True + + # Test case 4: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "false". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "false"} + ): + assert SubscriberClient._use_client_cert_effective() is False + + # Test case 5: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "True". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "True"}): + assert SubscriberClient._use_client_cert_effective() is True + + # Test case 6: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "False". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "False"} + ): + assert SubscriberClient._use_client_cert_effective() is False + + # Test case 7: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "TRUE". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "TRUE"}): + assert SubscriberClient._use_client_cert_effective() is True + + # Test case 8: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to "FALSE". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "FALSE"} + ): + assert SubscriberClient._use_client_cert_effective() is False + + # Test case 9: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not set. + # In this case, the method should return False, which is the default value. + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict(os.environ, clear=True): + assert SubscriberClient._use_client_cert_effective() is False + + # Test case 10: Test when `should_use_client_cert` is unavailable and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value. + # The method should raise a ValueError as the environment variable must be either + # "true" or "false". + if not hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"} + ): + with pytest.raises(ValueError): + SubscriberClient._use_client_cert_effective() + + # Test case 11: Test when `should_use_client_cert` is available and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is set to an invalid value. + # The method should return False as the environment variable is set to an invalid value. + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "unsupported"} + ): + assert SubscriberClient._use_client_cert_effective() is False + + # Test case 12: Test when `should_use_client_cert` is available and the + # `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is unset. Also, + # the GOOGLE_API_CONFIG environment variable is unset. + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": ""}): + with mock.patch.dict(os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": ""}): + assert SubscriberClient._use_client_cert_effective() is False + + def test__get_client_cert_source(): mock_provided_cert_source = mock.Mock() mock_default_cert_source = mock.Mock() @@ -552,17 +658,6 @@ def test_subscriber_client_client_options( == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" ) - # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} - ): - with pytest.raises(ValueError) as excinfo: - client = client_class(transport=transport_name) - assert ( - str(excinfo.value) - == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" - ) - # Check the case quota_project_id is provided options = client_options.ClientOptions(quota_project_id="octopus") with mock.patch.object(transport_class, "__init__") as patched: @@ -774,6 +869,119 @@ def test_subscriber_client_get_mtls_endpoint_and_cert_source(client_class): assert api_endpoint == mock_api_endpoint assert cert_source is None + # Test the case GOOGLE_API_USE_CLIENT_CERTIFICATE is "Unsupported". + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + mock_client_cert_source = mock.Mock() + mock_api_endpoint = "foo" + options = client_options.ClientOptions( + client_cert_source=mock_client_cert_source, + api_endpoint=mock_api_endpoint, + ) + api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source( + options + ) + assert api_endpoint == mock_api_endpoint + assert cert_source is None + + # Test cases for mTLS enablement when GOOGLE_API_USE_CLIENT_CERTIFICATE is unset. + test_cases = [ + ( + # With workloads present in config, mTLS is enabled. + { + "version": 1, + "cert_configs": { + "workload": { + "cert_path": "path/to/cert/file", + "key_path": "path/to/key/file", + } + }, + }, + mock_client_cert_source, + ), + ( + # With workloads not present in config, mTLS is disabled. + { + "version": 1, + "cert_configs": {}, + }, + None, + ), + ] + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + for config_data, expected_cert_source in test_cases: + env = os.environ.copy() + env.pop("GOOGLE_API_USE_CLIENT_CERTIFICATE", None) + with mock.patch.dict(os.environ, env, clear=True): + config_filename = "mock_certificate_config.json" + config_file_content = json.dumps(config_data) + m = mock.mock_open(read_data=config_file_content) + with mock.patch("builtins.open", m): + with mock.patch.dict( + os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": config_filename} + ): + mock_api_endpoint = "foo" + options = client_options.ClientOptions( + client_cert_source=mock_client_cert_source, + api_endpoint=mock_api_endpoint, + ) + ( + api_endpoint, + cert_source, + ) = client_class.get_mtls_endpoint_and_cert_source(options) + assert api_endpoint == mock_api_endpoint + assert cert_source is expected_cert_source + + # Test cases for mTLS enablement when GOOGLE_API_USE_CLIENT_CERTIFICATE is unset(empty). + test_cases = [ + ( + # With workloads present in config, mTLS is enabled. + { + "version": 1, + "cert_configs": { + "workload": { + "cert_path": "path/to/cert/file", + "key_path": "path/to/key/file", + } + }, + }, + mock_client_cert_source, + ), + ( + # With workloads not present in config, mTLS is disabled. + { + "version": 1, + "cert_configs": {}, + }, + None, + ), + ] + if hasattr(google.auth.transport.mtls, "should_use_client_cert"): + for config_data, expected_cert_source in test_cases: + env = os.environ.copy() + env.pop("GOOGLE_API_USE_CLIENT_CERTIFICATE", "") + with mock.patch.dict(os.environ, env, clear=True): + config_filename = "mock_certificate_config.json" + config_file_content = json.dumps(config_data) + m = mock.mock_open(read_data=config_file_content) + with mock.patch("builtins.open", m): + with mock.patch.dict( + os.environ, {"GOOGLE_API_CERTIFICATE_CONFIG": config_filename} + ): + mock_api_endpoint = "foo" + options = client_options.ClientOptions( + client_cert_source=mock_client_cert_source, + api_endpoint=mock_api_endpoint, + ) + ( + api_endpoint, + cert_source, + ) = client_class.get_mtls_endpoint_and_cert_source(options) + assert api_endpoint == mock_api_endpoint + assert cert_source is expected_cert_source + # Test the case GOOGLE_API_USE_MTLS_ENDPOINT is "never". with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): api_endpoint, cert_source = client_class.get_mtls_endpoint_and_cert_source() @@ -824,18 +1032,6 @@ def test_subscriber_client_get_mtls_endpoint_and_cert_source(client_class): == "Environment variable `GOOGLE_API_USE_MTLS_ENDPOINT` must be `never`, `auto` or `always`" ) - # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} - ): - with pytest.raises(ValueError) as excinfo: - client_class.get_mtls_endpoint_and_cert_source() - - assert ( - str(excinfo.value) - == "Environment variable `GOOGLE_API_USE_CLIENT_CERTIFICATE` must be either `true` or `false`" - ) - @pytest.mark.parametrize("client_class", [SubscriberClient, SubscriberAsyncClient]) @mock.patch.object( @@ -13114,6 +13310,7 @@ def test_subscriber_grpc_asyncio_transport_channel(): # Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are # removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.parametrize( "transport_class", [transports.SubscriberGrpcTransport, transports.SubscriberGrpcAsyncIOTransport], From 171f7d41cc749114c8e1ab9f618d1cb4ca8c0ac0 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Tue, 16 Dec 2025 14:31:54 -0800 Subject: [PATCH 182/190] chore: librarian release pull request: 20251216T133533Z (#1567) PR created by the Librarian CLI to initialize a release. Merging this PR will auto trigger a release. Librarian Version: v0.7.0 Language Image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209
google-cloud-pubsub: 2.34.0 ## [2.34.0](https://github.com/googleapis/python-pubsub/compare/v2.33.0...v2.34.0) (2025-12-16) ### Features * support mTLS certificates when available (#1566) ([24761a2f](https://github.com/googleapis/python-pubsub/commit/24761a2f))
--- .librarian/state.yaml | 2 +- CHANGELOG.md | 7 +++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.librarian/state.yaml b/.librarian/state.yaml index 9a7ad85b3..286fb52ef 100644 --- a/.librarian/state.yaml +++ b/.librarian/state.yaml @@ -1,7 +1,7 @@ image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209 libraries: - id: google-cloud-pubsub - version: 2.33.0 + version: 2.34.0 last_generated_commit: 9fcfbea0aa5b50fa22e190faceb073d74504172b apis: - path: google/pubsub/v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index ac71546a8..bcf798e60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.34.0](https://github.com/googleapis/python-pubsub/compare/v2.33.0...v2.34.0) (2025-12-16) + + +### Features + +* support mTLS certificates when available (#1566) ([24761a2fedeb17f5af98a72a62306ad59306a553](https://github.com/googleapis/python-pubsub/commit/24761a2fedeb17f5af98a72a62306ad59306a553)) + ## [2.33.0](https://github.com/googleapis/python-pubsub/compare/v2.32.0...v2.33.0) (2025-10-30) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index 1eb64a9d6..b31b170e1 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.33.0" # {x-release-please-version} +__version__ = "2.34.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index 1eb64a9d6..b31b170e1 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.33.0" # {x-release-please-version} +__version__ = "2.34.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index bcad2272f..f3af602ab 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.33.0" + "version": "2.34.0" }, "snippets": [ { From 13f8341478ea97f6ed8b5e7f7e7f75500da665c9 Mon Sep 17 00:00:00 2001 From: Andrew Browne <81702808+abbrowne126@users.noreply.github.com> Date: Fri, 23 Jan 2026 14:30:46 -0500 Subject: [PATCH 183/190] Chore: Remove Py3.7+3.8 Support (#1573) Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/python-pubsub/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea - [x] Ensure the tests and linter pass - [x] Code coverage does not decrease (if any source code was changed) - [x] Appropriate docs were updated (if necessary) Fixes #1563 --- .github/sync-repo-settings.yaml | 4 -- .github/workflows/docs.yml | 4 +- .github/workflows/unittest.yml | 2 +- .kokoro/samples/python3.7/common.cfg | 40 ------------------- .kokoro/samples/python3.7/continuous.cfg | 6 --- .kokoro/samples/python3.7/periodic-head.cfg | 11 ----- .kokoro/samples/python3.7/periodic.cfg | 6 --- .kokoro/samples/python3.7/presubmit.cfg | 6 --- .kokoro/samples/python3.8/common.cfg | 40 ------------------- .kokoro/samples/python3.8/continuous.cfg | 6 --- .kokoro/samples/python3.8/periodic-head.cfg | 11 ----- .kokoro/samples/python3.8/periodic.cfg | 6 --- .kokoro/samples/python3.8/presubmit.cfg | 6 --- .librarian/generator-input/librarian.py | 2 +- .librarian/generator-input/noxfile.py | 2 - .librarian/generator-input/setup.py | 10 ++--- CONTRIBUTING.rst | 10 ++--- README.rst | 12 +++--- librarian.py | 14 +++++-- mypy.ini | 2 +- noxfile.py | 2 - pytest.ini | 14 +------ samples/snippets/noxfile.py | 3 +- samples/snippets/requirements-test.txt | 7 +--- samples/snippets/requirements.txt | 12 ++---- .../templates/install_deps.tmpl.rst | 2 +- setup.py | 10 ++--- testing/constraints-3.10.txt | 12 +++--- testing/constraints-3.11.txt | 12 +++--- testing/constraints-3.12.txt | 12 +++--- testing/constraints-3.13.txt | 8 +--- testing/constraints-3.14.txt | 8 +--- testing/constraints-3.7.txt | 13 ------ testing/constraints-3.8.txt | 7 ---- testing/constraints-3.9.txt | 12 +++--- tests/system.py | 7 +--- tests/unit/gapic/pubsub_v1/test_publisher.py | 8 +--- .../gapic/pubsub_v1/test_schema_service.py | 7 +--- tests/unit/gapic/pubsub_v1/test_subscriber.py | 7 +--- .../pubsub_v1/publisher/batch/test_thread.py | 6 +-- .../sequencer/test_ordered_sequencer.py | 8 +--- .../sequencer/test_unordered_sequencer.py | 8 +--- .../publisher/test_publisher_client.py | 6 +-- .../pubsub_v1/subscriber/test_dispatcher.py | 6 +-- .../subscriber/test_futures_subscriber.py | 8 +--- .../pubsub_v1/subscriber/test_heartbeater.py | 7 +--- .../subscriber/test_helper_threads.py | 8 +--- .../unit/pubsub_v1/subscriber/test_leaser.py | 7 +--- .../unit/pubsub_v1/subscriber/test_message.py | 7 +--- .../pubsub_v1/subscriber/test_scheduler.py | 7 +--- .../subscriber/test_streaming_pull_manager.py | 6 +-- .../test_subscribe_opentelemetry.py | 7 +--- .../subscriber/test_subscriber_client.py | 6 +-- tests/unit/pubsub_v1/test_futures.py | 6 +-- 54 files changed, 89 insertions(+), 377 deletions(-) delete mode 100644 .kokoro/samples/python3.7/common.cfg delete mode 100644 .kokoro/samples/python3.7/continuous.cfg delete mode 100644 .kokoro/samples/python3.7/periodic-head.cfg delete mode 100644 .kokoro/samples/python3.7/periodic.cfg delete mode 100644 .kokoro/samples/python3.7/presubmit.cfg delete mode 100644 .kokoro/samples/python3.8/common.cfg delete mode 100644 .kokoro/samples/python3.8/continuous.cfg delete mode 100644 .kokoro/samples/python3.8/periodic-head.cfg delete mode 100644 .kokoro/samples/python3.8/periodic.cfg delete mode 100644 .kokoro/samples/python3.8/presubmit.cfg delete mode 100644 testing/constraints-3.7.txt delete mode 100644 testing/constraints-3.8.txt diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml index bfde18cc0..ecc31984d 100644 --- a/.github/sync-repo-settings.yaml +++ b/.github/sync-repo-settings.yaml @@ -11,8 +11,6 @@ branchProtectionRules: - 'Kokoro - Against Pub/Sub Lite samples' - 'cla/google' - 'Samples - Lint' - - 'Samples - Python 3.7' - - 'Samples - Python 3.8' - 'Samples - Python 3.9' - 'Samples - Python 3.10' - 'Samples - Python 3.11' @@ -21,8 +19,6 @@ branchProtectionRules: - 'docs' - 'docfx' - 'lint' - - 'unit (3.7)' - - 'unit (3.8)' - 'unit (3.9)' - 'unit (3.10)' - 'unit (3.11)' diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 0d0fdb861..c5ee98837 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup Python uses: actions/setup-python@v6 with: @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup Python uses: actions/setup-python@v6 with: diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index 04e411304..9fb410b81 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] + python: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.kokoro/samples/python3.7/common.cfg b/.kokoro/samples/python3.7/common.cfg deleted file mode 100644 index 9156c5975..000000000 --- a/.kokoro/samples/python3.7/common.cfg +++ /dev/null @@ -1,40 +0,0 @@ -# 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.7" -} - -# Declare build specific Cloud project. -env_vars: { - key: "BUILD_SPECIFIC_GCLOUD_PROJECT" - value: "python-docs-samples-tests-py37" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-pubsub/.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-pubsub/.kokoro/trampoline_v2.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.7/continuous.cfg b/.kokoro/samples/python3.7/continuous.cfg deleted file mode 100644 index a1c8d9759..000000000 --- a/.kokoro/samples/python3.7/continuous.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# 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.7/periodic-head.cfg b/.kokoro/samples/python3.7/periodic-head.cfg deleted file mode 100644 index f9cfcd33e..000000000 --- a/.kokoro/samples/python3.7/periodic-head.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# 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-pubsub/.kokoro/test-samples-against-head.sh" -} diff --git a/.kokoro/samples/python3.7/periodic.cfg b/.kokoro/samples/python3.7/periodic.cfg deleted file mode 100644 index 71cd1e597..000000000 --- a/.kokoro/samples/python3.7/periodic.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "False" -} diff --git a/.kokoro/samples/python3.7/presubmit.cfg b/.kokoro/samples/python3.7/presubmit.cfg deleted file mode 100644 index a1c8d9759..000000000 --- a/.kokoro/samples/python3.7/presubmit.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# 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.8/common.cfg b/.kokoro/samples/python3.8/common.cfg deleted file mode 100644 index 5922bef07..000000000 --- a/.kokoro/samples/python3.8/common.cfg +++ /dev/null @@ -1,40 +0,0 @@ -# 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.8" -} - -# Declare build specific Cloud project. -env_vars: { - key: "BUILD_SPECIFIC_GCLOUD_PROJECT" - value: "python-docs-samples-tests-py38" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-pubsub/.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-pubsub/.kokoro/trampoline_v2.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.8/continuous.cfg b/.kokoro/samples/python3.8/continuous.cfg deleted file mode 100644 index a1c8d9759..000000000 --- a/.kokoro/samples/python3.8/continuous.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# 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.8/periodic-head.cfg b/.kokoro/samples/python3.8/periodic-head.cfg deleted file mode 100644 index f9cfcd33e..000000000 --- a/.kokoro/samples/python3.8/periodic-head.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# 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-pubsub/.kokoro/test-samples-against-head.sh" -} diff --git a/.kokoro/samples/python3.8/periodic.cfg b/.kokoro/samples/python3.8/periodic.cfg deleted file mode 100644 index 71cd1e597..000000000 --- a/.kokoro/samples/python3.8/periodic.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "False" -} diff --git a/.kokoro/samples/python3.8/presubmit.cfg b/.kokoro/samples/python3.8/presubmit.cfg deleted file mode 100644 index a1c8d9759..000000000 --- a/.kokoro/samples/python3.8/presubmit.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# 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/.librarian/generator-input/librarian.py b/.librarian/generator-input/librarian.py index 5e6af7955..612003377 100644 --- a/.librarian/generator-input/librarian.py +++ b/.librarian/generator-input/librarian.py @@ -326,7 +326,7 @@ if count < 1: raise Exception(".coveragerc replacement failed.") - s.move([library], excludes=["noxfile.py", "README.rst", "docs/**/*", "setup.py", "testing/constraints-3.7.txt", "testing/constraints-3.8.txt"]) + s.move([library], excludes=["noxfile.py", "README.rst", "docs/**/*", "setup.py"]) s.remove_staging_dirs() # ---------------------------------------------------------------------------- diff --git a/.librarian/generator-input/noxfile.py b/.librarian/generator-input/noxfile.py index fd552166c..170360d6f 100644 --- a/.librarian/generator-input/noxfile.py +++ b/.librarian/generator-input/noxfile.py @@ -37,8 +37,6 @@ DEFAULT_PYTHON_VERSION = "3.14" UNIT_TEST_PYTHON_VERSIONS: List[str] = [ - "3.7", - "3.8", "3.9", "3.10", "3.11", diff --git a/.librarian/generator-input/setup.py b/.librarian/generator-input/setup.py index dd2809f82..761bc90ce 100644 --- a/.librarian/generator-input/setup.py +++ b/.librarian/generator-input/setup.py @@ -47,10 +47,8 @@ "protobuf>=3.20.2,<7.0.0,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5", "grpc-google-iam-v1 >= 0.12.4, < 1.0.0", "grpcio-status >= 1.33.2", - "opentelemetry-api <= 1.22.0; python_version<='3.7'", - "opentelemetry-api >= 1.27.0; python_version>='3.8'", - "opentelemetry-sdk <= 1.22.0; python_version<='3.7'", - "opentelemetry-sdk >= 1.27.0; python_version>='3.8'", + "opentelemetry-api >= 1.27.0", + "opentelemetry-sdk >= 1.27.0", ] extras = {"libcst": "libcst >= 0.3.10"} url = "https://github.com/googleapis/python-pubsub" @@ -82,8 +80,6 @@ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -97,7 +93,7 @@ packages=packages, install_requires=dependencies, extras_require=extras, - python_requires=">=3.7", + python_requires=">=3.9", include_package_data=True, zip_safe=False, ) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 417b1e9f8..4e926536b 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -22,7 +22,7 @@ In order to add a feature: documentation. - The feature must work fully on the following CPython versions: - 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13 and 3.14 on both UNIX and Windows. + 3.9, 3.10, 3.11, 3.12, 3.13 and 3.14 on both UNIX and Windows. - The feature must not add unnecessary dependencies (where "unnecessary" is of course subjective, but new dependencies should @@ -195,11 +195,11 @@ configure them just like the System Tests. # Run all tests in a folder $ cd samples/snippets - $ nox -s py-3.8 + $ nox -s py-3.14 # Run a single sample test $ cd samples/snippets - $ nox -s py-3.8 -- -k + $ nox -s py-3.14 -- -k ******************************************** Note About ``README`` as it pertains to PyPI @@ -221,8 +221,6 @@ Supported Python Versions We support: -- `Python 3.7`_ -- `Python 3.8`_ - `Python 3.9`_ - `Python 3.10`_ - `Python 3.11`_ @@ -230,8 +228,6 @@ We support: - `Python 3.13`_ - `Python 3.14`_ -.. _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/ diff --git a/README.rst b/README.rst index 97010e998..86ea7e48e 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ Python Client for Google Cloud Pub / Sub ======================================== -|GA| |pypi| |versions| +|GA| |pypi| |versions| `Google Cloud Pub / Sub`_ is a fully-managed real-time messaging service that allows you to send and receive messages between independent applications. You @@ -60,11 +60,13 @@ dependencies. Supported Python Versions ^^^^^^^^^^^^^^^^^^^^^^^^^ -Python >= 3.7 +Python >= 3.9 Deprecated Python Versions ^^^^^^^^^^^^^^^^^^^^^^^^^^ -Python <= 3.6. +Python < 3.9 + +The last version of this library compatible with Python 3.7 and 3.8 is google-cloud-pubsub==2.34.0. The last version of this library compatible with Python 2.7 is google-cloud-pubsub==1.7.0. @@ -146,7 +148,7 @@ the topic, and subscribe to that, passing a callback function. with pubsub_v1.SubscriberClient() as subscriber: subscriber.create_subscription( - name=subscription_name, topic=topic_name) + name=subscription_name, topic=topic_name) future = subscriber.subscribe(subscription_name, callback) The future returned by the call to ``subscriber.subscribe`` can be used to @@ -190,7 +192,7 @@ For example, to use JSON Web Tokens, provide a `google.auth.jwt.Credentials`_ in # The same for the publisher, except that the "audience" claim needs to be adjusted publisher_audience = "https://pubsub.googleapis.com/google.pubsub.v1.Publisher" - credentials_pub = credentials.with_claims(audience=publisher_audience) + credentials_pub = credentials.with_claims(audience=publisher_audience) publisher = pubsub_v1.PublisherClient(credentials=credentials_pub) .. _Credentials: https://google-auth.readthedocs.io/en/latest/reference/google.auth.credentials.html#google.auth.credentials.Credentials diff --git a/librarian.py b/librarian.py index 5e6af7955..ecaa36d0e 100644 --- a/librarian.py +++ b/librarian.py @@ -326,7 +326,15 @@ if count < 1: raise Exception(".coveragerc replacement failed.") - s.move([library], excludes=["noxfile.py", "README.rst", "docs/**/*", "setup.py", "testing/constraints-3.7.txt", "testing/constraints-3.8.txt"]) + s.move( + [library], + excludes=[ + "noxfile.py", + "README.rst", + "docs/**/*", + "setup.py", + ], + ) s.remove_staging_dirs() # ---------------------------------------------------------------------------- @@ -338,10 +346,10 @@ samples=True, cov_level=99, versions=gcp.common.detect_versions(path="./google", default_first=True), - unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"], + unit_test_python_versions=["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"], unit_test_dependencies=["flaky"], system_test_python_versions=["3.12"], - system_test_external_dependencies=["psutil","flaky"], + system_test_external_dependencies=["psutil", "flaky"], ) s.move(templated_files, excludes=[".coveragerc", ".github/**", "README.rst", "docs/**", ".kokoro/**"]) diff --git a/mypy.ini b/mypy.ini index 574c5aed3..a3cb5c292 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,3 +1,3 @@ [mypy] -python_version = 3.7 +python_version = 3.14 namespace_packages = True diff --git a/noxfile.py b/noxfile.py index d1b3c15d1..d7fa4a7e8 100644 --- a/noxfile.py +++ b/noxfile.py @@ -41,8 +41,6 @@ DEFAULT_PYTHON_VERSION = "3.14" UNIT_TEST_PYTHON_VERSIONS: List[str] = [ - "3.7", - "3.8", "3.9", "3.10", "3.11", diff --git a/pytest.ini b/pytest.ini index a1f4f115e..41cad40d6 100644 --- a/pytest.ini +++ b/pytest.ini @@ -6,8 +6,6 @@ filterwarnings = ignore:.*custom tp_new.*in Python 3.14:DeprecationWarning # Remove once https://github.com/grpc/grpc/issues/35086 is fixed ignore:There is no current event loop:DeprecationWarning:grpc.aio._channel - # Remove after support for Python 3.7 is dropped - ignore:After January 1, 2024, new releases of this library will drop support for Python 3.7:DeprecationWarning # Remove warning once https://github.com/googleapis/gapic-generator-python/issues/1938 is fixed ignore:The return_immediately flag is deprecated and should be set to False.:DeprecationWarning # Remove warning once https://github.com/googleapis/gapic-generator-python/issues/1939 is fixed @@ -26,13 +24,5 @@ filterwarnings = ignore:The `credentials_file` argument is deprecated because of a potential security risk:DeprecationWarning ignore:You are using a Python version.*which Google will stop supporting in new releases of google\.api_core.*:FutureWarning ignore:You are using a non-supported Python version \(([\d\.]+)\)\. Google will not post any further updates to google\.api_core.*:FutureWarning - ignore:You are using a Python version \(([\d\.]+)\) past its end of life\. Google will update google\.api_core.*:FutureWarning - # Remove after support for Python 3.7 is dropped - ignore:You are using a non-supported Python version \(3\.7:FutureWarning - # Remove after support for Python 3.8 is dropped - ignore:You are using a non-supported Python version \(3\.8:DeprecationWarning - ignore:You are using a non-supported Python version \(3\.8:FutureWarning - # Remove after support for Python 3.9 is dropped - ignore:You are using a Python version \(3\.9:FutureWarning - # Remove after support for Python 3.10 is dropped - ignore:.*You are using a Python version \(3\.10:FutureWarning + # These google library EOL warnings for Python versions don't matter for the purposes of a test. + ignore::FutureWarning:google.*: diff --git a/samples/snippets/noxfile.py b/samples/snippets/noxfile.py index c326375be..50f2fce56 100644 --- a/samples/snippets/noxfile.py +++ b/samples/snippets/noxfile.py @@ -87,9 +87,8 @@ def get_pytest_env_vars() -> Dict[str, str]: return ret -# DO NOT EDIT - automatically generated. # All versions used to test samples. -ALL_VERSIONS = ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] +ALL_VERSIONS = ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] # Any default versions that should be ignored. IGNORED_VERSIONS = TEST_CONFIG["ignored_versions"] diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index 7659e3676..a4969d6fe 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1,9 +1,6 @@ backoff==2.2.1 -pytest===7.4.4; python_version == '3.7' -pytest===8.3.5; python_version == '3.8' -pytest==8.4.2; python_version >= '3.9' +pytest==8.4.2 mock==5.2.0 flaky==3.8.1 -google-cloud-bigquery===3.30.0; python_version <= '3.8' -google-cloud-bigquery==3.38.0; python_version >= '3.9' +google-cloud-bigquery==3.38.0 google-cloud-storage==3.4.0 diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 63a78cd67..997d5a201 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,13 +1,7 @@ google-cloud-pubsub==2.31.1 avro==1.12.0 -protobuf===4.24.4; python_version == '3.7' -protobuf===5.29.4; python_version == '3.8' -protobuf==6.32.1; python_version >= '3.9' +protobuf==6.32.1 avro==1.12.0 -opentelemetry-api===1.22.0; python_version == '3.7' -opentelemetry-sdk===1.22.0; python_version == '3.7' -opentelemetry-api===1.33.1; python_version == '3.8' -opentelemetry-sdk===1.33.1; python_version == '3.8' -opentelemetry-api==1.37.0; python_version >= '3.9' -opentelemetry-sdk==1.37.0; python_version >= '3.9' +opentelemetry-api==1.37.0 +opentelemetry-sdk==1.37.0 opentelemetry-exporter-gcp-trace==1.9.0 diff --git a/scripts/readme-gen/templates/install_deps.tmpl.rst b/scripts/readme-gen/templates/install_deps.tmpl.rst index 6f069c6c8..f21db80c4 100644 --- a/scripts/readme-gen/templates/install_deps.tmpl.rst +++ b/scripts/readme-gen/templates/install_deps.tmpl.rst @@ -12,7 +12,7 @@ Install Dependencies .. _Python Development Environment Setup Guide: https://cloud.google.com/python/setup -#. Create a virtualenv. Samples are compatible with Python 3.7+. +#. Create a virtualenv. Samples are compatible with Python 3.9+. .. code-block:: bash diff --git a/setup.py b/setup.py index 74a11ebf6..211a3306f 100644 --- a/setup.py +++ b/setup.py @@ -51,10 +51,8 @@ "protobuf>=3.20.2,<7.0.0,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5", "grpc-google-iam-v1 >= 0.12.4, < 1.0.0", "grpcio-status >= 1.33.2", - "opentelemetry-api <= 1.22.0; python_version<='3.7'", - "opentelemetry-api >= 1.27.0; python_version>='3.8'", - "opentelemetry-sdk <= 1.22.0; python_version<='3.7'", - "opentelemetry-sdk >= 1.27.0; python_version>='3.8'", + "opentelemetry-api >= 1.27.0", + "opentelemetry-sdk >= 1.27.0", ] extras = {"libcst": "libcst >= 0.3.10"} url = "https://github.com/googleapis/python-pubsub" @@ -86,8 +84,6 @@ "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -101,7 +97,7 @@ packages=packages, install_requires=dependencies, extras_require=extras, - python_requires=">=3.7", + python_requires=">=3.9", include_package_data=True, zip_safe=False, ) diff --git a/testing/constraints-3.10.txt b/testing/constraints-3.10.txt index ef1c92fff..cc0b74a08 100644 --- a/testing/constraints-3.10.txt +++ b/testing/constraints-3.10.txt @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # This constraints file is required for unit tests. # List all library dependencies and extras in this file. -google-api-core -google-auth -grpcio -proto-plus -protobuf -grpc-google-iam-v1 +google-api-core>=2 +google-auth>=2 +grpcio>=1 +proto-plus>=1 +protobuf>=6 +grpc-google-iam-v1>=0 diff --git a/testing/constraints-3.11.txt b/testing/constraints-3.11.txt index ef1c92fff..cc0b74a08 100644 --- a/testing/constraints-3.11.txt +++ b/testing/constraints-3.11.txt @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # This constraints file is required for unit tests. # List all library dependencies and extras in this file. -google-api-core -google-auth -grpcio -proto-plus -protobuf -grpc-google-iam-v1 +google-api-core>=2 +google-auth>=2 +grpcio>=1 +proto-plus>=1 +protobuf>=6 +grpc-google-iam-v1>=0 diff --git a/testing/constraints-3.12.txt b/testing/constraints-3.12.txt index ef1c92fff..cc0b74a08 100644 --- a/testing/constraints-3.12.txt +++ b/testing/constraints-3.12.txt @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # This constraints file is required for unit tests. # List all library dependencies and extras in this file. -google-api-core -google-auth -grpcio -proto-plus -protobuf -grpc-google-iam-v1 +google-api-core>=2 +google-auth>=2 +grpcio>=1 +proto-plus>=1 +protobuf>=6 +grpc-google-iam-v1>=0 diff --git a/testing/constraints-3.13.txt b/testing/constraints-3.13.txt index 2ae5a677e..cc0b74a08 100644 --- a/testing/constraints-3.13.txt +++ b/testing/constraints-3.13.txt @@ -1,10 +1,6 @@ -# We use the constraints file for the latest Python version -# (currently this file) to check that the latest -# major versions of dependencies are supported in setup.py. +# -*- coding: utf-8 -*- +# This constraints file is required for unit tests. # List all library dependencies and extras in this file. -# Require the latest major version be installed for each dependency. -# e.g., if setup.py has "google-cloud-foo >= 1.14.0, < 2.0.0", -# Then this file should have google-cloud-foo>=1 google-api-core>=2 google-auth>=2 grpcio>=1 diff --git a/testing/constraints-3.14.txt b/testing/constraints-3.14.txt index 2ae5a677e..cc0b74a08 100644 --- a/testing/constraints-3.14.txt +++ b/testing/constraints-3.14.txt @@ -1,10 +1,6 @@ -# We use the constraints file for the latest Python version -# (currently this file) to check that the latest -# major versions of dependencies are supported in setup.py. +# -*- coding: utf-8 -*- +# This constraints file is required for unit tests. # List all library dependencies and extras in this file. -# Require the latest major version be installed for each dependency. -# e.g., if setup.py has "google-cloud-foo >= 1.14.0, < 2.0.0", -# Then this file should have google-cloud-foo>=1 google-api-core>=2 google-auth>=2 grpcio>=1 diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt deleted file mode 100644 index 08db5de87..000000000 --- a/testing/constraints-3.7.txt +++ /dev/null @@ -1,13 +0,0 @@ -# This constraints file is used to check that lower bounds -# are correct in setup.py -# List all library dependencies and extras in this file. -# Pin the version to the lower bound. -# e.g., if setup.py has "google-cloud-foo >= 1.14.0, < 2.0.0dev", -# Then this file should have google-cloud-foo==1.14.0 -google-api-core==1.34.0 -google-auth==2.14.1 -proto-plus==1.22.0 -protobuf==3.20.2 -grpc-google-iam-v1==0.12.4 -grpcio==1.51.3 -grpcio-status==1.33.2 diff --git a/testing/constraints-3.8.txt b/testing/constraints-3.8.txt deleted file mode 100644 index 30520e2d0..000000000 --- a/testing/constraints-3.8.txt +++ /dev/null @@ -1,7 +0,0 @@ -# -*- coding: utf-8 -*- -# This constraints file is required for unit tests. -# List all library dependencies and extras in this file. -google-api-core==1.34.0 -proto-plus -protobuf -grpc-google-iam-v1 diff --git a/testing/constraints-3.9.txt b/testing/constraints-3.9.txt index ef1c92fff..cc0b74a08 100644 --- a/testing/constraints-3.9.txt +++ b/testing/constraints-3.9.txt @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # This constraints file is required for unit tests. # List all library dependencies and extras in this file. -google-api-core -google-auth -grpcio -proto-plus -protobuf -grpc-google-iam-v1 +google-api-core>=2 +google-auth>=2 +grpcio>=1 +proto-plus>=1 +protobuf>=6 +grpc-google-iam-v1>=0 diff --git a/tests/system.py b/tests/system.py index e1af74402..9db2a5e12 100644 --- a/tests/system.py +++ b/tests/system.py @@ -20,16 +20,11 @@ import operator as op import os import psutil -import sys import threading import time from typing import Any, Callable, cast, TypeVar -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock from flaky import flaky import pytest diff --git a/tests/unit/gapic/pubsub_v1/test_publisher.py b/tests/unit/gapic/pubsub_v1/test_publisher.py index 978021fcd..33c3afef4 100644 --- a/tests/unit/gapic/pubsub_v1/test_publisher.py +++ b/tests/unit/gapic/pubsub_v1/test_publisher.py @@ -15,12 +15,8 @@ # import os -# try/except added for compatibility with python < 3.8 -try: - from unittest import mock - from unittest.mock import AsyncMock # pragma: NO COVER -except ImportError: # pragma: NO COVER - import mock + +import mock import grpc from grpc.experimental import aio diff --git a/tests/unit/gapic/pubsub_v1/test_schema_service.py b/tests/unit/gapic/pubsub_v1/test_schema_service.py index f71b66805..76dd7b1f8 100644 --- a/tests/unit/gapic/pubsub_v1/test_schema_service.py +++ b/tests/unit/gapic/pubsub_v1/test_schema_service.py @@ -15,12 +15,7 @@ # import os -# try/except added for compatibility with python < 3.8 -try: - from unittest import mock - from unittest.mock import AsyncMock # pragma: NO COVER -except ImportError: # pragma: NO COVER - import mock +import mock import grpc from grpc.experimental import aio diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py index 816b04500..52fc8c133 100644 --- a/tests/unit/gapic/pubsub_v1/test_subscriber.py +++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py @@ -16,12 +16,7 @@ import os import warnings -# try/except added for compatibility with python < 3.8 -try: - from unittest import mock - from unittest.mock import AsyncMock # pragma: NO COVER -except ImportError: # pragma: NO COVER - import mock +import mock import grpc from grpc.experimental import aio diff --git a/tests/unit/pubsub_v1/publisher/batch/test_thread.py b/tests/unit/pubsub_v1/publisher/batch/test_thread.py index ad8fa376b..dc6d25fad 100644 --- a/tests/unit/pubsub_v1/publisher/batch/test_thread.py +++ b/tests/unit/pubsub_v1/publisher/batch/test_thread.py @@ -17,11 +17,7 @@ import threading import time -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock import pytest diff --git a/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py b/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py index 4377d1447..f7c166aab 100644 --- a/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py +++ b/tests/unit/pubsub_v1/publisher/sequencer/test_ordered_sequencer.py @@ -13,14 +13,8 @@ # limitations under the License. import concurrent.futures as futures -import sys - -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock import pytest from google.auth import credentials diff --git a/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py b/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py index 739bae3bd..054e66da0 100644 --- a/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py +++ b/tests/unit/pubsub_v1/publisher/sequencer/test_unordered_sequencer.py @@ -11,14 +11,8 @@ # 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 sys - -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock import pytest from google.auth import credentials diff --git a/tests/unit/pubsub_v1/publisher/test_publisher_client.py b/tests/unit/pubsub_v1/publisher/test_publisher_client.py index 651c040ba..cc417d492 100644 --- a/tests/unit/pubsub_v1/publisher/test_publisher_client.py +++ b/tests/unit/pubsub_v1/publisher/test_publisher_client.py @@ -21,11 +21,7 @@ import grpc import math -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock import pytest import time diff --git a/tests/unit/pubsub_v1/subscriber/test_dispatcher.py b/tests/unit/pubsub_v1/subscriber/test_dispatcher.py index 5483c48c5..23e1a6c18 100644 --- a/tests/unit/pubsub_v1/subscriber/test_dispatcher.py +++ b/tests/unit/pubsub_v1/subscriber/test_dispatcher.py @@ -29,11 +29,7 @@ ) from google.pubsub_v1.types import PubsubMessage -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock import pytest from google.cloud.pubsub_v1.subscriber.exceptions import ( diff --git a/tests/unit/pubsub_v1/subscriber/test_futures_subscriber.py b/tests/unit/pubsub_v1/subscriber/test_futures_subscriber.py index d10da6fb1..c4c539f96 100644 --- a/tests/unit/pubsub_v1/subscriber/test_futures_subscriber.py +++ b/tests/unit/pubsub_v1/subscriber/test_futures_subscriber.py @@ -13,14 +13,8 @@ # limitations under the License. from __future__ import absolute_import -import sys - -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock import pytest from google.cloud.pubsub_v1.subscriber import futures diff --git a/tests/unit/pubsub_v1/subscriber/test_heartbeater.py b/tests/unit/pubsub_v1/subscriber/test_heartbeater.py index 857152ac3..cd9fd9762 100644 --- a/tests/unit/pubsub_v1/subscriber/test_heartbeater.py +++ b/tests/unit/pubsub_v1/subscriber/test_heartbeater.py @@ -13,17 +13,12 @@ # limitations under the License. import logging -import sys import threading from google.cloud.pubsub_v1.subscriber._protocol import heartbeater from google.cloud.pubsub_v1.subscriber._protocol import streaming_pull_manager -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock import pytest diff --git a/tests/unit/pubsub_v1/subscriber/test_helper_threads.py b/tests/unit/pubsub_v1/subscriber/test_helper_threads.py index bfbaf3e56..54659a5a3 100644 --- a/tests/unit/pubsub_v1/subscriber/test_helper_threads.py +++ b/tests/unit/pubsub_v1/subscriber/test_helper_threads.py @@ -12,13 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import sys - -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock import queue diff --git a/tests/unit/pubsub_v1/subscriber/test_leaser.py b/tests/unit/pubsub_v1/subscriber/test_leaser.py index 606dcc2c9..3d2a96151 100644 --- a/tests/unit/pubsub_v1/subscriber/test_leaser.py +++ b/tests/unit/pubsub_v1/subscriber/test_leaser.py @@ -13,7 +13,6 @@ # limitations under the License. import logging -import sys import threading from google.cloud.pubsub_v1 import types @@ -27,11 +26,7 @@ ) from google.cloud.pubsub_v1.subscriber import message -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock import pytest diff --git a/tests/unit/pubsub_v1/subscriber/test_message.py b/tests/unit/pubsub_v1/subscriber/test_message.py index 03bdc1514..676536f01 100644 --- a/tests/unit/pubsub_v1/subscriber/test_message.py +++ b/tests/unit/pubsub_v1/subscriber/test_message.py @@ -14,14 +14,9 @@ import datetime import queue -import sys import time -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock from google.api_core import datetime_helpers from google.cloud.pubsub_v1.subscriber import message diff --git a/tests/unit/pubsub_v1/subscriber/test_scheduler.py b/tests/unit/pubsub_v1/subscriber/test_scheduler.py index 3ed1978c1..22bd53729 100644 --- a/tests/unit/pubsub_v1/subscriber/test_scheduler.py +++ b/tests/unit/pubsub_v1/subscriber/test_scheduler.py @@ -15,15 +15,10 @@ import concurrent.futures import queue import pytest -import sys import threading import time -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock from google.cloud.pubsub_v1.subscriber import scheduler diff --git a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py index b9561d747..953b882f8 100644 --- a/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py +++ b/tests/unit/pubsub_v1/subscriber/test_streaming_pull_manager.py @@ -32,11 +32,7 @@ from google.cloud.pubsub_v1.types import PubsubMessage -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock import pytest from google.api_core import bidi diff --git a/tests/unit/pubsub_v1/subscriber/test_subscribe_opentelemetry.py b/tests/unit/pubsub_v1/subscriber/test_subscribe_opentelemetry.py index 2fb89aa7c..efa3e4f71 100644 --- a/tests/unit/pubsub_v1/subscriber/test_subscribe_opentelemetry.py +++ b/tests/unit/pubsub_v1/subscriber/test_subscribe_opentelemetry.py @@ -14,7 +14,6 @@ import datetime import time -import sys import queue import pytest @@ -32,11 +31,7 @@ from google.cloud.pubsub_v1.subscriber.message import Message from google.cloud.pubsub_v1.types import PubsubMessage -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock RECEIVED = datetime.datetime(2012, 4, 21, 15, 0, tzinfo=datetime.timezone.utc) RECEIVED_SECONDS = datetime_helpers.to_milliseconds(RECEIVED) // 1000 diff --git a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py index 3d3ff0111..ecaf23cf9 100644 --- a/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py +++ b/tests/unit/pubsub_v1/subscriber/test_subscriber_client.py @@ -16,11 +16,7 @@ import grpc -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock import pytest diff --git a/tests/unit/pubsub_v1/test_futures.py b/tests/unit/pubsub_v1/test_futures.py index 5a4dad41a..d6af2f359 100644 --- a/tests/unit/pubsub_v1/test_futures.py +++ b/tests/unit/pubsub_v1/test_futures.py @@ -17,11 +17,7 @@ import threading import time -# special case python < 3.8 -if sys.version_info.major == 3 and sys.version_info.minor < 8: - import mock -else: - from unittest import mock +from unittest import mock import pytest From 0e0eda944bf97e9c934f4fce9a72cf7ab66b8ad7 Mon Sep 17 00:00:00 2001 From: gurusai-voleti Date: Tue, 3 Feb 2026 03:33:30 +0530 Subject: [PATCH 184/190] chore: Migrate gsutil usage to gcloud storage (#1574) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automated: Migrate {target_path} from gsutil to gcloud storage This CL is part of the on going effort to migrate from the legacy `gsutil` tool to the new and improved `gcloud storage` command-line interface. `gcloud storage` is the recommended and modern tool for interacting with Google Cloud Storage, offering better performance, unified authentication, and a more consistent command structure with other `gcloud` components. 🚀 ### Automation Details This change was **generated automatically** by an agent that targets users of `gsutil`. The transformations applied are based on the [gsutil to gcloud storage migration guide](http://go/gsutil-gcloud-storage-migration-guide). ### ⚠️ Action Required: Please Review and Test Carefully While we have based the automation on the migration guide, every use case is unique. **It is crucial that you thoroughly test these changes in environments appropriate to your use-case before merging.** Be aware of potential differences between `gsutil` and `gcloud storage` that could impact your workflows. For instance, the structure of command output may have changed, requiring updates to any scripts that parse it. Similarly, command behavior can differ subtly; the `gcloud storage rsync` command has a different file deletion logic than `gsutil rsync`, which could lead to unintended file deletions. Our migration guides can help guide you through a list of mappings and some notable differences between the two tools. Standard presubmit tests are run as part of this CL's workflow. **If you need to target an additional test workflow or require assistance with testing, please let us know.** Please verify that all your Cloud Storage operations continue to work as expected to avoid any potential disruptions in production. ### Support and Collaboration The `GCS CLI` team is here to help! If you encounter any issues, have a complex use case that this automated change doesn't cover, or face any other blockers, please don't hesitate to reach out. We are happy to work with you to test and adjust these changes as needed. **Contact:** `gcs-cli-hyd@google.com` We appreciate your partnership in this important migration effort! #gsutil-migration --- .kokoro/trampoline_v2.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh index 35fa52923..d03f92dfc 100755 --- a/.kokoro/trampoline_v2.sh +++ b/.kokoro/trampoline_v2.sh @@ -26,8 +26,8 @@ # To run this script, first download few files from gcs to /dev/shm. # (/dev/shm is passed into the container as KOKORO_GFILE_DIR). # -# gsutil cp gs://cloud-devrel-kokoro-resources/python-docs-samples/secrets_viewer_service_account.json /dev/shm -# gsutil cp gs://cloud-devrel-kokoro-resources/python-docs-samples/automl_secrets.txt /dev/shm +# gcloud storage cp gs://cloud-devrel-kokoro-resources/python-docs-samples/secrets_viewer_service_account.json /dev/shm +# gcloud storage cp gs://cloud-devrel-kokoro-resources/python-docs-samples/automl_secrets.txt /dev/shm # # Then run the script. # .kokoro/trampoline_v2.sh From 6fd28b04d0a572a31528aee74cb4484a7e07dffa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 11:00:57 -0800 Subject: [PATCH 185/190] build(deps): bump protobuf from 6.32.1 to 6.33.5 in /samples/snippets (#1577) Bumps [protobuf](https://github.com/protocolbuffers/protobuf) from 6.32.1 to 6.33.5.
Release notes

Sourced from protobuf's releases.

Protocol Buffers v34.0-rc1

Announcements

Bazel

Compiler

C++

... (truncated)

Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=protobuf&package-manager=pip&previous-version=6.32.1&new-version=6.33.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/googleapis/python-pubsub/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- samples/snippets/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 997d5a201..5ee669828 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,6 +1,6 @@ google-cloud-pubsub==2.31.1 avro==1.12.0 -protobuf==6.32.1 +protobuf==6.33.5 avro==1.12.0 opentelemetry-api==1.37.0 opentelemetry-sdk==1.37.0 From e4b4f5228f153d3263ff853e690b88f28d770c1d Mon Sep 17 00:00:00 2001 From: Andrew Browne <81702808+abbrowne126@users.noreply.github.com> Date: Thu, 5 Feb 2026 15:41:31 -0500 Subject: [PATCH 186/190] chore: bump dependencies manually (#1579) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: - [x] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/python-pubsub/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea - [x] Ensure the tests and linter pass - [x] Code coverage does not decrease (if any source code was changed) - [x] Appropriate docs were updated (if necessary) Fixes #1578 🦕 --- .github/workflows/lint.yml | 2 +- samples/snippets/publisher.py | 5 +---- samples/snippets/publisher_test.py | 5 +++-- samples/snippets/requirements-test.txt | 7 ++++--- samples/snippets/requirements.txt | 12 ++++++------ samples/snippets/subscriber.py | 2 +- samples/snippets/subscriber_test.py | 2 +- 7 files changed, 17 insertions(+), 18 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6204983fd..a52933488 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup Python uses: actions/setup-python@v6 with: diff --git a/samples/snippets/publisher.py b/samples/snippets/publisher.py index d2b6dd2b8..13a70bee6 100644 --- a/samples/snippets/publisher.py +++ b/samples/snippets/publisher.py @@ -1046,10 +1046,7 @@ def detach_subscription(project_id: str, subscription_id: str) -> None: args.gcp_service_account, ) elif args.command == "create_smt": - create_topic_with_smt( - args.project_id, - args.topic_id, - ) + create_topic_with_smt(args.project_id, f"{args.topic_id}-smt") elif args.command == "update_kinesis_ingestion": update_topic_type( args.project_id, diff --git a/samples/snippets/publisher_test.py b/samples/snippets/publisher_test.py index 1c691bd5c..43c6b2848 100644 --- a/samples/snippets/publisher_test.py +++ b/samples/snippets/publisher_test.py @@ -316,15 +316,16 @@ def test_create_topic_with_confluent_cloud_ingestion( def test_create_with_smt( publisher_client: pubsub_v1.PublisherClient, capsys: CaptureFixture[str] ) -> None: + smt_topic_name = f"{TOPIC_ID}-smt" # The scope of `topic_path` is limited to this function. - topic_path = publisher_client.topic_path(PROJECT_ID, TOPIC_ID) + topic_path = publisher_client.topic_path(PROJECT_ID, smt_topic_name) try: publisher_client.delete_topic(request={"topic": topic_path}) except NotFound: pass - publisher.create_topic_with_smt(PROJECT_ID, TOPIC_ID) + publisher.create_topic_with_smt(PROJECT_ID, smt_topic_name) out, _ = capsys.readouterr() assert f"Created topic: {topic_path} with SMT" in out diff --git a/samples/snippets/requirements-test.txt b/samples/snippets/requirements-test.txt index a4969d6fe..1aeb6d04c 100644 --- a/samples/snippets/requirements-test.txt +++ b/samples/snippets/requirements-test.txt @@ -1,6 +1,7 @@ backoff==2.2.1 -pytest==8.4.2 +pytest==8.4.2; python_version <= '3.9' +pytest==9.0.2; python_version > '3.9' mock==5.2.0 flaky==3.8.1 -google-cloud-bigquery==3.38.0 -google-cloud-storage==3.4.0 +google-cloud-bigquery==3.40.0 +google-cloud-storage==3.9.0 diff --git a/samples/snippets/requirements.txt b/samples/snippets/requirements.txt index 5ee669828..25029a9f9 100644 --- a/samples/snippets/requirements.txt +++ b/samples/snippets/requirements.txt @@ -1,7 +1,7 @@ -google-cloud-pubsub==2.31.1 -avro==1.12.0 +google-cloud-pubsub==2.34.0 +avro==1.12.1 protobuf==6.33.5 -avro==1.12.0 -opentelemetry-api==1.37.0 -opentelemetry-sdk==1.37.0 -opentelemetry-exporter-gcp-trace==1.9.0 +avro==1.12.1 +opentelemetry-api==1.39.1 +opentelemetry-sdk==1.39.1 +opentelemetry-exporter-gcp-trace==1.11.0 diff --git a/samples/snippets/subscriber.py b/samples/snippets/subscriber.py index 5549d056f..94d083ae1 100644 --- a/samples/snippets/subscriber.py +++ b/samples/snippets/subscriber.py @@ -1518,7 +1518,7 @@ def callback(message: pubsub_v1.subscriber.message.Message) -> None: ) elif args.command == "create-with-smt": create_subscription_with_smt( - args.project_id, args.topic_id, args.subscription_id + args.project_id, f"{args.topic_id}-smt", f"{args.subscription_id}-smt" ) elif args.command == "delete": diff --git a/samples/snippets/subscriber_test.py b/samples/snippets/subscriber_test.py index 53a844e01..d1f1db94c 100644 --- a/samples/snippets/subscriber_test.py +++ b/samples/snippets/subscriber_test.py @@ -585,7 +585,7 @@ def test_create_subscription_with_smt( capsys: CaptureFixture[str], ) -> None: subscription_for_create_name = ( - f"subscription-test-subscription-for-create-with-smt-{PY_VERSION}-{UUID}" + f"subscription-test-subscription-for-create-with-smt-{PY_VERSION}-{UUID}-smt" ) subscription_path = subscriber_client.subscription_path( From 07011139f51e4ff195889faf849bd707655e6d46 Mon Sep 17 00:00:00 2001 From: Andrew Browne <81702808+abbrowne126@users.noreply.github.com> Date: Thu, 5 Feb 2026 16:52:58 -0500 Subject: [PATCH 187/190] chore: librarian generate pull request: 20260205T210552Z (#1580) PR created by the Librarian CLI to generate Cloud Client Libraries code from protos. BEGIN_COMMIT BEGIN_NESTED_COMMIT feat: Add AIInference MessageTransform type PiperOrigin-RevId: 853856321 Library-IDs: google-cloud-pubsub Source-link: [googleapis/googleapis@9fb55c41](https://github.com/googleapis/googleapis/commit/9fb55c41) END_NESTED_COMMIT BEGIN_NESTED_COMMIT docs: A comment for field `topic` in message `.google.pubsub.v1.Subscription` is updated PiperOrigin-RevId: 853856321 Library-IDs: google-cloud-pubsub Source-link: [googleapis/googleapis@9fb55c41](https://github.com/googleapis/googleapis/commit/9fb55c41) END_NESTED_COMMIT BEGIN_NESTED_COMMIT docs: A comment for field `analytics_hub_subscription_info` in message `.google.pubsub.v1.Subscription` is updated PiperOrigin-RevId: 853856321 Library-IDs: google-cloud-pubsub Source-link: [googleapis/googleapis@9fb55c41](https://github.com/googleapis/googleapis/commit/9fb55c41) END_NESTED_COMMIT BEGIN_NESTED_COMMIT docs: A comment for field `subscription` in message `.google.pubsub.v1.CreateSnapshotRequest` is updated PiperOrigin-RevId: 853856321 Library-IDs: google-cloud-pubsub Source-link: [googleapis/googleapis@9fb55c41](https://github.com/googleapis/googleapis/commit/9fb55c41) END_NESTED_COMMIT BEGIN_NESTED_COMMIT docs: add tags documentation links to Pub/Sub resource tags fields PiperOrigin-RevId: 845891076 Library-IDs: google-cloud-pubsub Source-link: [googleapis/googleapis@ff251e77](https://github.com/googleapis/googleapis/commit/ff251e77) END_NESTED_COMMIT BEGIN_NESTED_COMMIT docs: Add the IDENTIFIER field behavior annotation to fields of Cloud Pub/Sub methods that represent a specific identity and need to be sourced with additional care PiperOrigin-RevId: 840763233 Library-IDs: google-cloud-pubsub Source-link: [googleapis/googleapis@d89bb8a1](https://github.com/googleapis/googleapis/commit/d89bb8a1) END_NESTED_COMMIT END_COMMIT This pull request is generated with proto changes between [googleapis/googleapis@9fcfbea0](https://github.com/googleapis/googleapis/commit/9fcfbea0aa5b50fa22e190faceb073d74504172b) (exclusive) and [googleapis/googleapis@9fb55c41](https://github.com/googleapis/googleapis/commit/9fb55c416c90ff9e14f7101d68394eb9fca5918b) (inclusive). Librarian Version: v0.8.0 Language Image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209 --- .librarian/generator-input/librarian.py | 4 +- .librarian/state.yaml | 2 +- google/pubsub/__init__.py | 2 + google/pubsub_v1/__init__.py | 2 + .../services/publisher/async_client.py | 16 +-- google/pubsub_v1/services/publisher/client.py | 16 +-- .../services/subscriber/async_client.py | 4 +- .../pubsub_v1/services/subscriber/client.py | 4 +- google/pubsub_v1/types/__init__.py | 2 + google/pubsub_v1/types/pubsub.py | 126 ++++++++++++++++-- testing/constraints-3.10.txt | 12 +- testing/constraints-3.11.txt | 12 +- testing/constraints-3.12.txt | 12 +- testing/constraints-3.13.txt | 8 +- testing/constraints-3.14.txt | 8 +- testing/constraints-3.9.txt | 12 +- tests/unit/gapic/pubsub_v1/test_publisher.py | 1 + tests/unit/gapic/pubsub_v1/test_subscriber.py | 1 + 18 files changed, 183 insertions(+), 61 deletions(-) diff --git a/.librarian/generator-input/librarian.py b/.librarian/generator-input/librarian.py index 612003377..5263c2285 100644 --- a/.librarian/generator-input/librarian.py +++ b/.librarian/generator-input/librarian.py @@ -338,10 +338,10 @@ samples=True, cov_level=99, versions=gcp.common.detect_versions(path="./google", default_first=True), - unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"], + unit_test_python_versions=["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"], unit_test_dependencies=["flaky"], system_test_python_versions=["3.12"], - system_test_external_dependencies=["psutil","flaky"], + system_test_external_dependencies=["psutil", "flaky"], ) s.move(templated_files, excludes=[".coveragerc", ".github/**", "README.rst", "docs/**", ".kokoro/**"]) diff --git a/.librarian/state.yaml b/.librarian/state.yaml index 286fb52ef..a8f6625a3 100644 --- a/.librarian/state.yaml +++ b/.librarian/state.yaml @@ -2,7 +2,7 @@ image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-li libraries: - id: google-cloud-pubsub version: 2.34.0 - last_generated_commit: 9fcfbea0aa5b50fa22e190faceb073d74504172b + last_generated_commit: 2d0b3a154fb4f56993487c5409850ad3431a2690 apis: - path: google/pubsub/v1 service_config: pubsub_v1.yaml diff --git a/google/pubsub/__init__.py b/google/pubsub/__init__.py index d88449a53..b61343e55 100644 --- a/google/pubsub/__init__.py +++ b/google/pubsub/__init__.py @@ -28,6 +28,7 @@ from google.pubsub_v1.services.subscriber.async_client import SubscriberAsyncClient from google.pubsub_v1.types.pubsub import AcknowledgeRequest +from google.pubsub_v1.types.pubsub import AIInference from google.pubsub_v1.types.pubsub import BigQueryConfig from google.pubsub_v1.types.pubsub import CloudStorageConfig from google.pubsub_v1.types.pubsub import CreateSnapshotRequest @@ -104,6 +105,7 @@ "SubscriberClient", "SubscriberAsyncClient", "AcknowledgeRequest", + "AIInference", "BigQueryConfig", "CloudStorageConfig", "CreateSnapshotRequest", diff --git a/google/pubsub_v1/__init__.py b/google/pubsub_v1/__init__.py index 00d6b495e..5d7a6518c 100644 --- a/google/pubsub_v1/__init__.py +++ b/google/pubsub_v1/__init__.py @@ -36,6 +36,7 @@ from .services.subscriber import SubscriberAsyncClient from .types.pubsub import AcknowledgeRequest +from .types.pubsub import AIInference from .types.pubsub import BigQueryConfig from .types.pubsub import CloudStorageConfig from .types.pubsub import CreateSnapshotRequest @@ -202,6 +203,7 @@ def _get_version(dependency_name): "PublisherAsyncClient", "SchemaServiceAsyncClient", "SubscriberAsyncClient", + "AIInference", "AcknowledgeRequest", "BigQueryConfig", "CloudStorageConfig", diff --git a/google/pubsub_v1/services/publisher/async_client.py b/google/pubsub_v1/services/publisher/async_client.py index 3767a460b..9f52347d4 100644 --- a/google/pubsub_v1/services/publisher/async_client.py +++ b/google/pubsub_v1/services/publisher/async_client.py @@ -339,14 +339,14 @@ async def sample_create_topic(): request (Optional[Union[google.pubsub_v1.types.Topic, dict]]): The request object. A topic resource. name (:class:`str`): - Required. The name of the topic. It must have the format - ``"projects/{project}/topics/{topic}"``. ``{topic}`` - must start with a letter, and contain only letters - (``[A-Za-z]``), numbers (``[0-9]``), dashes (``-``), - underscores (``_``), periods (``.``), tildes (``~``), - plus (``+``) or percent signs (``%``). It must be - between 3 and 255 characters in length, and it must not - start with ``"goog"``. + Required. Identifier. The name of the topic. It must + have the format ``"projects/{project}/topics/{topic}"``. + ``{topic}`` must start with a letter, and contain only + letters (``[A-Za-z]``), numbers (``[0-9]``), dashes + (``-``), underscores (``_``), periods (``.``), tildes + (``~``), plus (``+``) or percent signs (``%``). It must + be between 3 and 255 characters in length, and it must + not start with ``"goog"``. This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this diff --git a/google/pubsub_v1/services/publisher/client.py b/google/pubsub_v1/services/publisher/client.py index 27ed4dce8..7467b7540 100644 --- a/google/pubsub_v1/services/publisher/client.py +++ b/google/pubsub_v1/services/publisher/client.py @@ -868,14 +868,14 @@ def sample_create_topic(): request (Union[google.pubsub_v1.types.Topic, dict]): The request object. A topic resource. name (str): - Required. The name of the topic. It must have the format - ``"projects/{project}/topics/{topic}"``. ``{topic}`` - must start with a letter, and contain only letters - (``[A-Za-z]``), numbers (``[0-9]``), dashes (``-``), - underscores (``_``), periods (``.``), tildes (``~``), - plus (``+``) or percent signs (``%``). It must be - between 3 and 255 characters in length, and it must not - start with ``"goog"``. + Required. Identifier. The name of the topic. It must + have the format ``"projects/{project}/topics/{topic}"``. + ``{topic}`` must start with a letter, and contain only + letters (``[A-Za-z]``), numbers (``[0-9]``), dashes + (``-``), underscores (``_``), periods (``.``), tildes + (``~``), plus (``+``) or percent signs (``%``). It must + be between 3 and 255 characters in length, and it must + not start with ``"goog"``. This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this diff --git a/google/pubsub_v1/services/subscriber/async_client.py b/google/pubsub_v1/services/subscriber/async_client.py index 34843a0e0..a7f1cc3f5 100644 --- a/google/pubsub_v1/services/subscriber/async_client.py +++ b/google/pubsub_v1/services/subscriber/async_client.py @@ -360,8 +360,8 @@ async def sample_create_subscription(): then the subscriber will pull and ack messages using API methods. At most one of these fields may be set. name (:class:`str`): - Required. The name of the subscription. It must have the - format + Required. Identifier. The name of the subscription. It + must have the format ``"projects/{project}/subscriptions/{subscription}"``. ``{subscription}`` must start with a letter, and contain only letters (``[A-Za-z]``), numbers (``[0-9]``), dashes diff --git a/google/pubsub_v1/services/subscriber/client.py b/google/pubsub_v1/services/subscriber/client.py index 2a946f726..23e7ff6d0 100644 --- a/google/pubsub_v1/services/subscriber/client.py +++ b/google/pubsub_v1/services/subscriber/client.py @@ -872,8 +872,8 @@ def sample_create_subscription(): then the subscriber will pull and ack messages using API methods. At most one of these fields may be set. name (str): - Required. The name of the subscription. It must have the - format + Required. Identifier. The name of the subscription. It + must have the format ``"projects/{project}/subscriptions/{subscription}"``. ``{subscription}`` must start with a letter, and contain only letters (``[A-Za-z]``), numbers (``[0-9]``), dashes diff --git a/google/pubsub_v1/types/__init__.py b/google/pubsub_v1/types/__init__.py index 85c6b901b..593abc464 100644 --- a/google/pubsub_v1/types/__init__.py +++ b/google/pubsub_v1/types/__init__.py @@ -17,6 +17,7 @@ from .pubsub import ( AcknowledgeRequest, + AIInference, BigQueryConfig, CloudStorageConfig, CreateSnapshotRequest, @@ -99,6 +100,7 @@ __all__ = ( "TimeoutType", "AcknowledgeRequest", + "AIInference", "BigQueryConfig", "CloudStorageConfig", "CreateSnapshotRequest", diff --git a/google/pubsub_v1/types/pubsub.py b/google/pubsub_v1/types/pubsub.py index 26c13fb18..1a5663c29 100644 --- a/google/pubsub_v1/types/pubsub.py +++ b/google/pubsub_v1/types/pubsub.py @@ -21,6 +21,7 @@ from google.protobuf import duration_pb2 # type: ignore from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import struct_pb2 # type: ignore from google.protobuf import timestamp_pb2 # type: ignore from google.pubsub_v1.types import schema as gp_schema @@ -34,6 +35,7 @@ "PlatformLogsSettings", "IngestionFailureEvent", "JavaScriptUDF", + "AIInference", "MessageTransform", "Topic", "PubsubMessage", @@ -1344,9 +1346,78 @@ class JavaScriptUDF(proto.Message): ) +class AIInference(proto.Message): + r"""Configuration for making inference requests against Vertex AI + models. + + + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields + + Attributes: + endpoint (str): + Required. An endpoint to a Vertex AI model of the form + ``projects/{project}/locations/{location}/endpoints/{endpoint}`` + or + ``projects/{project}/locations/{location}/publishers/{publisher}/models/{model}``. + Vertex AI API requests will be sent to this endpoint. + unstructured_inference (google.pubsub_v1.types.AIInference.UnstructuredInference): + Optional. Requests and responses can be any + arbitrary JSON object. + + This field is a member of `oneof`_ ``inference_mode``. + service_account_email (str): + Optional. The service account to use to make prediction + requests against endpoints. The resource creator or updater + that specifies this field must have + ``iam.serviceAccounts.actAs`` permission on the service + account. If not specified, the Pub/Sub `service + agent <{$universe.dns_names.final_documentation_domain}/iam/docs/service-agents>`__, + service-{project_number}@gcp-sa-pubsub.iam.gserviceaccount.com, + is used. + """ + + class UnstructuredInference(proto.Message): + r"""Configuration for making inferences using arbitrary JSON + payloads. + + Attributes: + parameters (google.protobuf.struct_pb2.Struct): + Optional. A parameters object to be included + in each inference request. The parameters object + is combined with the data field of the Pub/Sub + message to form the inference request. + """ + + parameters: struct_pb2.Struct = proto.Field( + proto.MESSAGE, + number=1, + message=struct_pb2.Struct, + ) + + endpoint: str = proto.Field( + proto.STRING, + number=1, + ) + unstructured_inference: UnstructuredInference = proto.Field( + proto.MESSAGE, + number=2, + oneof="inference_mode", + message=UnstructuredInference, + ) + service_account_email: str = proto.Field( + proto.STRING, + number=3, + ) + + class MessageTransform(proto.Message): r"""All supported message transforms types. + This message has `oneof`_ fields (mutually exclusive fields). + For each oneof, at most one member field can be set at the same time. + Setting any member of the oneof automatically clears all other + members. + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields Attributes: @@ -1355,6 +1426,13 @@ class MessageTransform(proto.Message): JavaScriptUDF's are specified on a resource, each must have a unique ``function_name``. + This field is a member of `oneof`_ ``transform``. + ai_inference (google.pubsub_v1.types.AIInference): + Optional. AI Inference. Specifies the Vertex + AI endpoint that inference requests built from + the Pub/Sub message data and provided parameters + will be sent to. + This field is a member of `oneof`_ ``transform``. enabled (bool): Optional. This field is deprecated, use the ``disabled`` @@ -1370,6 +1448,12 @@ class MessageTransform(proto.Message): oneof="transform", message="JavaScriptUDF", ) + ai_inference: "AIInference" = proto.Field( + proto.MESSAGE, + number=6, + oneof="transform", + message="AIInference", + ) enabled: bool = proto.Field( proto.BOOL, number=3, @@ -1385,10 +1469,10 @@ class Topic(proto.Message): Attributes: name (str): - Required. The name of the topic. It must have the format - ``"projects/{project}/topics/{topic}"``. ``{topic}`` must - start with a letter, and contain only letters - (``[A-Za-z]``), numbers (``[0-9]``), dashes (``-``), + Required. Identifier. The name of the topic. It must have + the format ``"projects/{project}/topics/{topic}"``. + ``{topic}`` must start with a letter, and contain only + letters (``[A-Za-z]``), numbers (``[0-9]``), dashes (``-``), underscores (``_``), periods (``.``), tildes (``~``), plus (``+``) or percent signs (``%``). It must be between 3 and 255 characters in length, and it must not start with @@ -1442,7 +1526,11 @@ class Topic(proto.Message): example: "123/environment": "production", - "123/costCenter": "marketing". + "123/costCenter": "marketing" + See + https://docs.cloud.google.com/pubsub/docs/tags + for more information on using tags with Pub/Sub + resources. """ class State(proto.Enum): @@ -1889,8 +1977,8 @@ class Subscription(proto.Message): Attributes: name (str): - Required. The name of the subscription. It must have the - format + Required. Identifier. The name of the subscription. It must + have the format ``"projects/{project}/subscriptions/{subscription}"``. ``{subscription}`` must start with a letter, and contain only letters (``[A-Za-z]``), numbers (``[0-9]``), dashes @@ -2043,7 +2131,7 @@ class Subscription(proto.Message): analytics_hub_subscription_info (google.pubsub_v1.types.Subscription.AnalyticsHubSubscriptionInfo): Output only. Information about the associated Analytics Hub subscription. Only set if the - subscritpion is created by Analytics Hub. + subscription is created by Analytics Hub. message_transforms (MutableSequence[google.pubsub_v1.types.MessageTransform]): Optional. Transforms to be applied to messages before they are delivered to @@ -2055,7 +2143,11 @@ class Subscription(proto.Message): example: "123/environment": "production", - "123/costCenter": "marketing". + "123/costCenter": "marketing" + See + https://docs.cloud.google.com/pubsub/docs/tags + for more information on using tags with Pub/Sub + resources. """ class State(proto.Enum): @@ -2530,6 +2622,10 @@ class State(proto.Enum): Cannot write to the destination because enforce_in_transit is set to true and the destination locations are not in the allowed regions. + VERTEX_AI_LOCATION_RESTRICTION (6): + Cannot write to the BigQuery table because the table is not + in the same location as where Vertex AI models used in + ``message_transform``\ s are deployed. """ STATE_UNSPECIFIED = 0 ACTIVE = 1 @@ -2537,6 +2633,7 @@ class State(proto.Enum): NOT_FOUND = 3 SCHEMA_MISMATCH = 4 IN_TRANSIT_LOCATION_RESTRICTION = 5 + VERTEX_AI_LOCATION_RESTRICTION = 6 table: str = proto.Field( proto.STRING, @@ -2663,6 +2760,10 @@ class State(proto.Enum): Cannot write to the Cloud Storage bucket due to an incompatibility between the topic schema and subscription settings. + VERTEX_AI_LOCATION_RESTRICTION (6): + Cannot write to the Cloud Storage bucket because the bucket + is not in the same location as where Vertex AI models used + in ``message_transform``\ s are deployed. """ STATE_UNSPECIFIED = 0 ACTIVE = 1 @@ -2670,6 +2771,7 @@ class State(proto.Enum): NOT_FOUND = 3 IN_TRANSIT_LOCATION_RESTRICTION = 4 SCHEMA_MISMATCH = 5 + VERTEX_AI_LOCATION_RESTRICTION = 6 class TextConfig(proto.Message): r"""Configuration for writing message data in text format. @@ -3354,7 +3456,11 @@ class CreateSnapshotRequest(proto.Message): example: "123/environment": "production", - "123/costCenter": "marketing". + "123/costCenter": "marketing" + See + https://docs.cloud.google.com/pubsub/docs/tags + for more information on using tags with Pub/Sub + resources. """ name: str = proto.Field( diff --git a/testing/constraints-3.10.txt b/testing/constraints-3.10.txt index cc0b74a08..ef1c92fff 100644 --- a/testing/constraints-3.10.txt +++ b/testing/constraints-3.10.txt @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # This constraints file is required for unit tests. # List all library dependencies and extras in this file. -google-api-core>=2 -google-auth>=2 -grpcio>=1 -proto-plus>=1 -protobuf>=6 -grpc-google-iam-v1>=0 +google-api-core +google-auth +grpcio +proto-plus +protobuf +grpc-google-iam-v1 diff --git a/testing/constraints-3.11.txt b/testing/constraints-3.11.txt index cc0b74a08..ef1c92fff 100644 --- a/testing/constraints-3.11.txt +++ b/testing/constraints-3.11.txt @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # This constraints file is required for unit tests. # List all library dependencies and extras in this file. -google-api-core>=2 -google-auth>=2 -grpcio>=1 -proto-plus>=1 -protobuf>=6 -grpc-google-iam-v1>=0 +google-api-core +google-auth +grpcio +proto-plus +protobuf +grpc-google-iam-v1 diff --git a/testing/constraints-3.12.txt b/testing/constraints-3.12.txt index cc0b74a08..ef1c92fff 100644 --- a/testing/constraints-3.12.txt +++ b/testing/constraints-3.12.txt @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # This constraints file is required for unit tests. # List all library dependencies and extras in this file. -google-api-core>=2 -google-auth>=2 -grpcio>=1 -proto-plus>=1 -protobuf>=6 -grpc-google-iam-v1>=0 +google-api-core +google-auth +grpcio +proto-plus +protobuf +grpc-google-iam-v1 diff --git a/testing/constraints-3.13.txt b/testing/constraints-3.13.txt index cc0b74a08..2ae5a677e 100644 --- a/testing/constraints-3.13.txt +++ b/testing/constraints-3.13.txt @@ -1,6 +1,10 @@ -# -*- coding: utf-8 -*- -# This constraints file is required for unit tests. +# We use the constraints file for the latest Python version +# (currently this file) to check that the latest +# major versions of dependencies are supported in setup.py. # List all library dependencies and extras in this file. +# Require the latest major version be installed for each dependency. +# e.g., if setup.py has "google-cloud-foo >= 1.14.0, < 2.0.0", +# Then this file should have google-cloud-foo>=1 google-api-core>=2 google-auth>=2 grpcio>=1 diff --git a/testing/constraints-3.14.txt b/testing/constraints-3.14.txt index cc0b74a08..2ae5a677e 100644 --- a/testing/constraints-3.14.txt +++ b/testing/constraints-3.14.txt @@ -1,6 +1,10 @@ -# -*- coding: utf-8 -*- -# This constraints file is required for unit tests. +# We use the constraints file for the latest Python version +# (currently this file) to check that the latest +# major versions of dependencies are supported in setup.py. # List all library dependencies and extras in this file. +# Require the latest major version be installed for each dependency. +# e.g., if setup.py has "google-cloud-foo >= 1.14.0, < 2.0.0", +# Then this file should have google-cloud-foo>=1 google-api-core>=2 google-auth>=2 grpcio>=1 diff --git a/testing/constraints-3.9.txt b/testing/constraints-3.9.txt index cc0b74a08..ef1c92fff 100644 --- a/testing/constraints-3.9.txt +++ b/testing/constraints-3.9.txt @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- # This constraints file is required for unit tests. # List all library dependencies and extras in this file. -google-api-core>=2 -google-auth>=2 -grpcio>=1 -proto-plus>=1 -protobuf>=6 -grpc-google-iam-v1>=0 +google-api-core +google-auth +grpcio +proto-plus +protobuf +grpc-google-iam-v1 diff --git a/tests/unit/gapic/pubsub_v1/test_publisher.py b/tests/unit/gapic/pubsub_v1/test_publisher.py index 33c3afef4..0edfbd382 100644 --- a/tests/unit/gapic/pubsub_v1/test_publisher.py +++ b/tests/unit/gapic/pubsub_v1/test_publisher.py @@ -55,6 +55,7 @@ from google.oauth2 import service_account from google.protobuf import duration_pb2 # type: ignore from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import struct_pb2 # type: ignore from google.protobuf import timestamp_pb2 # type: ignore from google.pubsub_v1.services.publisher import PublisherAsyncClient from google.pubsub_v1.services.publisher import PublisherClient diff --git a/tests/unit/gapic/pubsub_v1/test_subscriber.py b/tests/unit/gapic/pubsub_v1/test_subscriber.py index 52fc8c133..1aa2e55c9 100644 --- a/tests/unit/gapic/pubsub_v1/test_subscriber.py +++ b/tests/unit/gapic/pubsub_v1/test_subscriber.py @@ -55,6 +55,7 @@ from google.oauth2 import service_account from google.protobuf import duration_pb2 # type: ignore from google.protobuf import field_mask_pb2 # type: ignore +from google.protobuf import struct_pb2 # type: ignore from google.protobuf import timestamp_pb2 # type: ignore from google.pubsub_v1.services.subscriber import SubscriberAsyncClient from google.pubsub_v1.services.subscriber import SubscriberClient From 63796ff3def3658ea22d4c2b734c08ad5ed94e72 Mon Sep 17 00:00:00 2001 From: Andrew Browne <81702808+abbrowne126@users.noreply.github.com> Date: Thu, 5 Feb 2026 17:20:42 -0500 Subject: [PATCH 188/190] chore: librarian release pull request: 20260205T215747Z (#1581) PR created by the Librarian CLI to initialize a release. Merging this PR will auto trigger a release. Librarian Version: v0.8.0 Language Image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209
google-cloud-pubsub: 2.35.0 ## [2.35.0](https://github.com/googleapis/python-pubsub/compare/v2.34.0...v2.35.0) (2026-02-05) ### Features * Add AIInference MessageTransform type (PiperOrigin-RevId: 853856321) ([07011139](https://github.com/googleapis/python-pubsub/commit/07011139)) ### Documentation * A comment for field `analytics_hub_subscription_info` in message `.google.pubsub.v1.Subscription` is updated (PiperOrigin-RevId: 853856321) ([07011139](https://github.com/googleapis/python-pubsub/commit/07011139)) * Add the IDENTIFIER field behavior annotation to fields of Cloud Pub/Sub methods that represent a specific identity and need to be sourced with additional care (PiperOrigin-RevId: 840763233) ([07011139](https://github.com/googleapis/python-pubsub/commit/07011139)) * A comment for field `topic` in message `.google.pubsub.v1.Subscription` is updated (PiperOrigin-RevId: 853856321) ([07011139](https://github.com/googleapis/python-pubsub/commit/07011139)) * A comment for field `subscription` in message `.google.pubsub.v1.CreateSnapshotRequest` is updated (PiperOrigin-RevId: 853856321) ([07011139](https://github.com/googleapis/python-pubsub/commit/07011139)) * add tags documentation links to Pub/Sub resource tags fields (PiperOrigin-RevId: 845891076) ([07011139](https://github.com/googleapis/python-pubsub/commit/07011139))
--- .librarian/state.yaml | 2 +- CHANGELOG.md | 16 ++++++++++++++++ google/pubsub/gapic_version.py | 2 +- google/pubsub_v1/gapic_version.py | 2 +- .../snippet_metadata_google.pubsub.v1.json | 2 +- 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.librarian/state.yaml b/.librarian/state.yaml index a8f6625a3..2d5b23ac6 100644 --- a/.librarian/state.yaml +++ b/.librarian/state.yaml @@ -1,7 +1,7 @@ image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:b8058df4c45e9a6e07f6b4d65b458d0d059241dd34c814f151c8bf6b89211209 libraries: - id: google-cloud-pubsub - version: 2.34.0 + version: 2.35.0 last_generated_commit: 2d0b3a154fb4f56993487c5409850ad3431a2690 apis: - path: google/pubsub/v1 diff --git a/CHANGELOG.md b/CHANGELOG.md index bcf798e60..bef0c4c9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ [1]: https://pypi.org/project/google-cloud-pubsub/#history +## [2.35.0](https://github.com/googleapis/python-pubsub/compare/v2.34.0...v2.35.0) (2026-02-05) + + +### Documentation + +* A comment for field `topic` in message `.google.pubsub.v1.Subscription` is updated ([07011139f51e4ff195889faf849bd707655e6d46](https://github.com/googleapis/python-pubsub/commit/07011139f51e4ff195889faf849bd707655e6d46)) +* A comment for field `analytics_hub_subscription_info` in message `.google.pubsub.v1.Subscription` is updated ([07011139f51e4ff195889faf849bd707655e6d46](https://github.com/googleapis/python-pubsub/commit/07011139f51e4ff195889faf849bd707655e6d46)) +* A comment for field `subscription` in message `.google.pubsub.v1.CreateSnapshotRequest` is updated ([07011139f51e4ff195889faf849bd707655e6d46](https://github.com/googleapis/python-pubsub/commit/07011139f51e4ff195889faf849bd707655e6d46)) +* add tags documentation links to Pub/Sub resource tags fields ([07011139f51e4ff195889faf849bd707655e6d46](https://github.com/googleapis/python-pubsub/commit/07011139f51e4ff195889faf849bd707655e6d46)) +* Add the IDENTIFIER field behavior annotation to fields of Cloud Pub/Sub methods that represent a specific identity and need to be sourced with additional care ([07011139f51e4ff195889faf849bd707655e6d46](https://github.com/googleapis/python-pubsub/commit/07011139f51e4ff195889faf849bd707655e6d46)) + + +### Features + +* Add AIInference MessageTransform type ([07011139f51e4ff195889faf849bd707655e6d46](https://github.com/googleapis/python-pubsub/commit/07011139f51e4ff195889faf849bd707655e6d46)) + ## [2.34.0](https://github.com/googleapis/python-pubsub/compare/v2.33.0...v2.34.0) (2025-12-16) diff --git a/google/pubsub/gapic_version.py b/google/pubsub/gapic_version.py index b31b170e1..6d72a226d 100644 --- a/google/pubsub/gapic_version.py +++ b/google/pubsub/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.34.0" # {x-release-please-version} +__version__ = "2.35.0" # {x-release-please-version} diff --git a/google/pubsub_v1/gapic_version.py b/google/pubsub_v1/gapic_version.py index b31b170e1..6d72a226d 100644 --- a/google/pubsub_v1/gapic_version.py +++ b/google/pubsub_v1/gapic_version.py @@ -13,4 +13,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "2.34.0" # {x-release-please-version} +__version__ = "2.35.0" # {x-release-please-version} diff --git a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json index f3af602ab..2a423fd86 100644 --- a/samples/generated_samples/snippet_metadata_google.pubsub.v1.json +++ b/samples/generated_samples/snippet_metadata_google.pubsub.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-pubsub", - "version": "2.34.0" + "version": "2.35.0" }, "snippets": [ { From 1cc7e7f69043dc4417324e722b1f5c9d3c123d4c Mon Sep 17 00:00:00 2001 From: Tomo Suzuki Date: Mon, 23 Feb 2026 11:15:07 -0500 Subject: [PATCH 189/190] chore: replace old teams with cloud-sdk-python-team and pubsub-team (#1582) This PR replaces the old yoshi-python team with cloud-sdk-python-team and api-pubsub with pubsub-team. b/478003109 --- .github/CODEOWNERS | 8 ++++---- .repo-metadata.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f1b33465e..a01d0971f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,8 +5,8 @@ # https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax # Note: This file is autogenerated. To make changes to the codeowner team, please update .repo-metadata.json. -# @googleapis/yoshi-python @googleapis/api-pubsub are the default owners for changes in this repo -* @googleapis/yoshi-python @googleapis/api-pubsub +# @googleapis/cloud-sdk-python-team @googleapis/pubsub-team are the default owners for changes in this repo +* @googleapis/cloud-sdk-python-team @googleapis/pubsub-team -# @googleapis/python-samples-reviewers @googleapis/api-pubsub are the default owners for samples changes -/samples/ @googleapis/python-samples-reviewers @googleapis/api-pubsub +# @googleapis/python-samples-reviewers @googleapis/pubsub-team are the default owners for samples changes +/samples/ @googleapis/python-samples-reviewers @googleapis/pubsub-team diff --git a/.repo-metadata.json b/.repo-metadata.json index 8d12e4cc0..f65318dba 100644 --- a/.repo-metadata.json +++ b/.repo-metadata.json @@ -11,7 +11,7 @@ "api_id": "pubsub.googleapis.com", "requires_billing": true, "default_version": "v1", - "codeowner_team": "@googleapis/api-pubsub", + "codeowner_team": "@googleapis/cloud-sdk-python-team @googleapis/pubsub-team", "api_shortname": "pubsub", "library_type": "GAPIC_COMBO", "api_description": "is designed to provide reliable, many-to-many, asynchronous messaging between applications. Publisher applications can send messages to a topic and other applications can subscribe to that topic to receive the messages. By decoupling senders and receivers, Google Cloud Pub/Sub allows developers to communicate between independently written applications." From f26371dd0e5e3af6e4b90e6c98b26267a17821f9 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Mon, 9 Mar 2026 15:42:28 -0400 Subject: [PATCH 190/190] build: update README to indicate that source has moved (#1591) Towards https://github.com/googleapis/google-cloud-python/issues/10930 --- README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rst b/README.rst index 86ea7e48e..bf78f1635 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,8 @@ +:**NOTE**: **This github repository is archived. The repository contents and history have moved to** `google-cloud-python`_. + +.. _google-cloud-python: https://github.com/googleapis/google-cloud-python/tree/main/packages/google-cloud-pubsub + + Python Client for Google Cloud Pub / Sub ========================================