verify_python_release.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 #Copyright 2019 gRPC authors.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 """Verifies that all gRPC Python artifacts have been successfully published.
17 
18 This script is intended to be run from a directory containing the artifacts
19 that have been uploaded and only the artifacts that have been uploaded. We use
20 PyPI's JSON API to verify that the proper filenames and checksums are present.
21 
22 Note that PyPI may take several minutes to update its metadata. Don't have a
23 heart attack immediately.
24 
25 This sanity check is a good first step, but ideally, we would automate the
26 entire release process.
27 """
28 
29 import argparse
30 import collections
31 import hashlib
32 import os
33 import sys
34 
35 import requests
36 
37 _DEFAULT_PACKAGES = [
38  "grpcio",
39  "grpcio-tools",
40  "grpcio-status",
41  "grpcio-health-checking",
42  "grpcio-reflection",
43  "grpcio-channelz",
44  "grpcio-testing",
45  "grpcio-admin",
46  "grpcio-csds",
47 ]
48 
49 Artifact = collections.namedtuple("Artifact", ("filename", "checksum"))
50 
51 
52 def _get_md5_checksum(filename):
53  """Calculate the md5sum for a file."""
54  hash_md5 = hashlib.md5()
55  with open(filename, 'rb') as f:
56  for chunk in iter(lambda: f.read(4096), b""):
57  hash_md5.update(chunk)
58  return hash_md5.hexdigest()
59 
60 
62  """Get a set of artifacts representing all files in the cwd."""
63  return set(
64  Artifact(f, _get_md5_checksum(f)) for f in os.listdir(os.getcwd()))
65 
66 
67 def _get_remote_artifacts_for_package(package, version):
68  """Get a list of artifacts based on PyPi's json metadata.
69 
70  Note that this data will not updated immediately after upload. In my
71  experience, it has taken a minute on average to be fresh.
72  """
73  artifacts = set()
74  payload = requests.get("https://pypi.org/pypi/{}/{}/json".format(
75  package, version)).json()
76  for download_info in payload['releases'][version]:
77  artifacts.add(
78  Artifact(download_info['filename'], download_info['md5_digest']))
79  return artifacts
80 
81 
82 def _get_remote_artifacts_for_packages(packages, version):
83  artifacts = set()
84  for package in packages:
85  artifacts |= _get_remote_artifacts_for_package(package, version)
86  return artifacts
87 
88 
89 def _verify_release(version, packages):
90  """Compare the local artifacts to the packages uploaded to PyPI."""
91  local_artifacts = _get_local_artifacts()
92  remote_artifacts = _get_remote_artifacts_for_packages(packages, version)
93  if local_artifacts != remote_artifacts:
94  local_but_not_remote = local_artifacts - remote_artifacts
95  remote_but_not_local = remote_artifacts - local_artifacts
96  if local_but_not_remote:
97  print("The following artifacts exist locally but not remotely.")
98  for artifact in local_but_not_remote:
99  print(artifact)
100  if remote_but_not_local:
101  print("The following artifacts exist remotely but not locally.")
102  for artifact in remote_but_not_local:
103  print(artifact)
104  sys.exit(1)
105  print("Release verified successfully.")
106 
107 
108 if __name__ == "__main__":
109  parser = argparse.ArgumentParser(
110  "Verify a release. Run this from a directory containing only the"
111  "artifacts to be uploaded. Note that PyPI may take several minutes"
112  "after the upload to reflect the proper metadata.")
113  parser.add_argument("version")
114  parser.add_argument("packages",
115  nargs='*',
116  type=str,
117  default=_DEFAULT_PACKAGES)
118  args = parser.parse_args()
119  _verify_release(args.version, args.packages)
verify_python_release._get_remote_artifacts_for_package
def _get_remote_artifacts_for_package(package, version)
Definition: verify_python_release.py:67
http2_test_server.format
format
Definition: http2_test_server.py:118
verify_python_release._verify_release
def _verify_release(version, packages)
Definition: verify_python_release.py:89
verify_python_release._get_remote_artifacts_for_packages
def _get_remote_artifacts_for_packages(packages, version)
Definition: verify_python_release.py:82
verify_python_release._get_local_artifacts
def _get_local_artifacts()
Definition: verify_python_release.py:61
verify_python_release._get_md5_checksum
def _get_md5_checksum(filename)
Definition: verify_python_release.py:52
cpp.gmock_class.set
set
Definition: bloaty/third_party/googletest/googlemock/scripts/generator/cpp/gmock_class.py:44
open
#define open
Definition: test-fs.c:46
iter
Definition: test_winkernel.cpp:47
verify_python_release.Artifact
Artifact
Definition: verify_python_release.py:49


grpc
Author(s):
autogenerated on Thu Mar 13 2025 03:01:51