Skip to content

Commit b2853c6

Browse files
authored
chore: update script for generating README (#12689)
Towards #11147 Towards #12679 🦕
1 parent 63b5f56 commit b2853c6

1 file changed

Lines changed: 103 additions & 44 deletions

File tree

scripts/updateapilist.py

Lines changed: 103 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,64 @@
1717
import os
1818
import requests
1919
from typing import List, Optional
20+
from dataclasses import dataclass
2021

2122

2223
class MissingGithubToken(ValueError):
2324
"""Raised when the GITHUB_TOKEN environment variable is not set"""
2425

2526
pass
2627

28+
RAW_CONTENT_BASE_URL = "https://raw.githubusercontent.com"
29+
MONO_REPO_PATH_FORMAT = "googleapis/google-cloud-python/main/packages/{repo_slug}"
30+
SPLIT_REPO_PATH_FORMAT = "{repo_slug}/main"
31+
REPO_METADATA_FILENAME = ".repo-metadata.json"
32+
33+
34+
# MONO_REPO defines the name of the mono repository for Python.
35+
MONO_REPO = "googleapis/google-cloud-python"
36+
37+
# REPO_EXCLUSION lists the repositories that need to be excluded.
38+
REPO_EXCLUSION = [
39+
# core libraries
40+
"googleapis/python-api-core",
41+
"googleapis/python-cloud-core",
42+
# proto only packages
43+
"googleapis/python-api-common-protos",
44+
# testing utilities
45+
"googleapis/python-test-utils",
46+
]
47+
48+
# PACKAGE_RESPONSE_KEY defines the package name in the response.
49+
PACKAGE_RESPONSE_KEY = "name"
50+
51+
# REPO_RESPONSE_KEY defines the repository name in the response.
52+
REPO_RESPONSE_KEY = "full_name"
53+
54+
# ARCHIVED_RESPONSE_KEY defines the repository archived status in the response.
55+
ARCHIVED_RESPONSE_KEY = "archived"
56+
57+
# BASE_API defines the base API for Github.
58+
BASE_API = "https://api.github.com"
59+
60+
61+
62+
63+
2764
class CloudClient:
2865
repo: str = None
2966
title: str = None
3067
release_level: str = None
3168
distribution_name: str = None
69+
issue_tracker: str = None
3270

3371
def __init__(self, repo: dict):
3472
self.repo = repo["repo"]
3573
# For now, strip out "Google Cloud" to standardize the titles
3674
self.title = repo["name_pretty"].replace("Google ", "").replace("Cloud ", "")
3775
self.release_level = repo["release_level"]
3876
self.distribution_name = repo["distribution_name"]
77+
self.issue_tracker = repo.get("issue_tracker")
3978

4079
# For sorting, we want to sort by release level, then API pretty_name
4180
def __lt__(self, other):
@@ -48,6 +87,24 @@ def __repr__(self):
4887
return repr((self.release_level, self.title))
4988

5089

90+
@dataclass
91+
class Extractor:
92+
path_format: str
93+
response_key: str
94+
95+
def client_for_repo(self, repo_slug) -> Optional[CloudClient]:
96+
path = self.path_format.format(repo_slug=repo_slug)
97+
url = f"{RAW_CONTENT_BASE_URL}/{path}/{REPO_METADATA_FILENAME}"
98+
response = requests.get(url)
99+
if response.status_code != requests.codes.ok:
100+
return
101+
102+
return CloudClient(response.json())
103+
104+
def get_clients_from_batch_response(self, response_json) -> List[CloudClient]:
105+
return [self.client_for_repo(repo[self.response_key]) for repo in response_json if allowed_repo(repo)]
106+
107+
51108
def replace_content_in_readme(content_rows: List[str]) -> None:
52109
START_MARKER = ".. API_TABLE_START"
53110
END_MARKER = ".. API_TABLE_END"
@@ -74,13 +131,20 @@ def replace_content_in_readme(content_rows: List[str]) -> None:
74131
def client_row(client: CloudClient) -> str:
75132
pypi_badge = f""".. |PyPI-{client.distribution_name}| image:: https://img.shields.io/pypi/v/{client.distribution_name}.svg
76133
:target: https://pypi.org/project/{client.distribution_name}\n"""
134+
135+
url = f"https://github.com/{client.repo}"
136+
if client.repo == MONO_REPO:
137+
url += f"/tree/main/packages/{client.distribution_name}"
77138

78139
content_row = [
79-
f" * - `{client.title} <https://github.com/{client.repo}>`_\n",
80-
f" - " + "|" + client.release_level + "|\n"
81-
f" - |PyPI-{client.distribution_name}|\n",
140+
f" * - `{client.title} <{url}>`_\n",
141+
f" - " + client.release_level + "\n",
142+
f" - |PyPI-{client.distribution_name}|\n",
82143
]
83144

145+
if client.issue_tracker:
146+
content_row.append(f" - `API Issues <{client.issue_tracker}>`_\n")
147+
84148
return (content_row, pypi_badge)
85149

86150

@@ -93,6 +157,7 @@ def generate_table_contents(clients: List[CloudClient]) -> List[str]:
93157
" * - Client\n",
94158
" - Release Level\n",
95159
" - Version\n",
160+
" - API Issue Tracker\n",
96161
]
97162

98163
pypi_links = ["\n"]
@@ -104,52 +169,30 @@ def generate_table_contents(clients: List[CloudClient]) -> List[str]:
104169
return content_rows + pypi_links
105170

106171

107-
REPO_METADATA_URL_FORMAT = (
108-
"https://raw.githubusercontent.com/{repo_slug}/main/.repo-metadata.json"
109-
)
110-
111-
112-
def client_for_repo(repo_slug) -> Optional[CloudClient]:
113-
url = REPO_METADATA_URL_FORMAT.format(repo_slug=repo_slug)
114-
response = requests.get(url)
115-
if response.status_code != requests.codes.ok:
116-
return
117-
118-
return CloudClient(response.json())
119-
120-
REPO_EXCLUSION = [
121-
# core libraries
122-
"googleapis/python-api-core",
123-
"googleapis/python-cloud-core",
124-
# proto only packages
125-
"googleapis/python-org-policy",
126-
"googleapis/python-os-config",
127-
"googleapis/python-access-context-manager",
128-
"googleapis/python-api-common-protos",
129-
# testing utilities
130-
"googleapis/python-test-utils",
131-
]
132-
133-
134172
def allowed_repo(repo) -> bool:
135-
return (
136-
repo["full_name"].startswith("googleapis/python-")
137-
and repo["full_name"] not in REPO_EXCLUSION
138-
and not repo["archived"]
173+
return REPO_RESPONSE_KEY not in repo or (
174+
repo[REPO_RESPONSE_KEY].startswith("googleapis/python-")
175+
and repo[REPO_RESPONSE_KEY] not in REPO_EXCLUSION
176+
and not repo[ARCHIVED_RESPONSE_KEY]
139177
)
140178

141179

142-
def get_clients_batch_from_response_json(response_json) -> List[CloudClient]:
143-
return [client_for_repo(repo["full_name"]) for repo in response_json if allowed_repo(repo)]
180+
def mono_repo_clients(token: str) -> List[CloudClient]:
181+
# all mono repo clients
182+
url = f"{BASE_API}/repos/{MONO_REPO}/contents/packages"
183+
headers = {'Authorization': f'token {token}'}
184+
response = requests.get(url=url, headers=headers)
185+
mono_repo_extractor = Extractor(path_format=MONO_REPO_PATH_FORMAT, response_key=PACKAGE_RESPONSE_KEY)
186+
187+
return mono_repo_extractor.get_clients_from_batch_response(response.json())
144188

145-
def all_clients() -> List[CloudClient]:
146-
clients = []
147-
first_request = True
148-
token = os.environ['GITHUB_TOKEN']
149189

190+
def split_repo_clients(token: str) -> List[CloudClient]:
191+
192+
first_request = True
150193
while first_request or 'next' in response.links:
151194
if first_request:
152-
url = "https://api.github.com/search/repositories?page=1"
195+
url = f"{BASE_API}/search/repositories?page=1"
153196
first_request = False
154197
else:
155198
url = response.links['next']['url']
@@ -159,13 +202,29 @@ def all_clients() -> List[CloudClient]:
159202
repositories = response.json().get("items", [])
160203
if len(repositories) == 0:
161204
break
162-
clients.extend(get_clients_batch_from_response_json(repositories))
205+
206+
split_repo_extractor = Extractor(path_format=SPLIT_REPO_PATH_FORMAT, response_key=REPO_RESPONSE_KEY)
207+
return split_repo_extractor.get_clients_from_batch_response(repositories)
208+
209+
210+
def get_token():
211+
if 'GITHUB_TOKEN' not in os.environ:
212+
raise MissingGithubToken("Please include a GITHUB_TOKEN env var.")
213+
214+
token = os.environ['GITHUB_TOKEN']
215+
return token
216+
217+
218+
def all_clients() -> List[CloudClient]:
219+
clients = []
220+
token = get_token()
221+
222+
clients.extend(split_repo_clients(token))
223+
clients.extend(mono_repo_clients(token))
163224

164225
# remove empty clients
165226
return [client for client in clients if client]
166227

167-
if 'GITHUB_TOKEN' not in os.environ:
168-
raise MissingGithubToken("Please include a GITHUB_TOKEN env var.")
169228

170229
clients = sorted(all_clients())
171230
table_contents = generate_table_contents(clients)

0 commit comments

Comments
 (0)