xref: /aosp_15_r20/external/pytorch/tools/github/github_utils.py (revision da0073e96a02ea20f0ac840b70461e3646d07c45)
1*da0073e9SAndroid Build Coastguard Worker"""GitHub Utilities"""
2*da0073e9SAndroid Build Coastguard Worker
3*da0073e9SAndroid Build Coastguard Workerfrom __future__ import annotations
4*da0073e9SAndroid Build Coastguard Worker
5*da0073e9SAndroid Build Coastguard Workerimport json
6*da0073e9SAndroid Build Coastguard Workerimport os
7*da0073e9SAndroid Build Coastguard Workerfrom typing import Any, Callable, cast, Dict
8*da0073e9SAndroid Build Coastguard Workerfrom urllib.error import HTTPError
9*da0073e9SAndroid Build Coastguard Workerfrom urllib.parse import quote
10*da0073e9SAndroid Build Coastguard Workerfrom urllib.request import Request, urlopen
11*da0073e9SAndroid Build Coastguard Worker
12*da0073e9SAndroid Build Coastguard Worker
13*da0073e9SAndroid Build Coastguard Workerdef gh_fetch_url_and_headers(
14*da0073e9SAndroid Build Coastguard Worker    url: str,
15*da0073e9SAndroid Build Coastguard Worker    *,
16*da0073e9SAndroid Build Coastguard Worker    headers: dict[str, str] | None = None,
17*da0073e9SAndroid Build Coastguard Worker    data: dict[str, Any] | None = None,
18*da0073e9SAndroid Build Coastguard Worker    method: str | None = None,
19*da0073e9SAndroid Build Coastguard Worker    reader: Callable[[Any], Any] = lambda x: x.read(),
20*da0073e9SAndroid Build Coastguard Worker) -> tuple[Any, Any]:
21*da0073e9SAndroid Build Coastguard Worker    if headers is None:
22*da0073e9SAndroid Build Coastguard Worker        headers = {}
23*da0073e9SAndroid Build Coastguard Worker    token = os.environ.get("GITHUB_TOKEN")
24*da0073e9SAndroid Build Coastguard Worker    if token is not None and url.startswith("https://api.github.com/"):
25*da0073e9SAndroid Build Coastguard Worker        headers["Authorization"] = f"token {token}"
26*da0073e9SAndroid Build Coastguard Worker    data_ = json.dumps(data).encode() if data is not None else None
27*da0073e9SAndroid Build Coastguard Worker    try:
28*da0073e9SAndroid Build Coastguard Worker        with urlopen(Request(url, headers=headers, data=data_, method=method)) as conn:
29*da0073e9SAndroid Build Coastguard Worker            return conn.headers, reader(conn)
30*da0073e9SAndroid Build Coastguard Worker    except HTTPError as err:
31*da0073e9SAndroid Build Coastguard Worker        if err.code == 403 and all(
32*da0073e9SAndroid Build Coastguard Worker            key in err.headers for key in ["X-RateLimit-Limit", "X-RateLimit-Used"]
33*da0073e9SAndroid Build Coastguard Worker        ):
34*da0073e9SAndroid Build Coastguard Worker            print(
35*da0073e9SAndroid Build Coastguard Worker                f"""Rate limit exceeded:
36*da0073e9SAndroid Build Coastguard Worker                Used: {err.headers['X-RateLimit-Used']}
37*da0073e9SAndroid Build Coastguard Worker                Limit: {err.headers['X-RateLimit-Limit']}
38*da0073e9SAndroid Build Coastguard Worker                Remaining: {err.headers['X-RateLimit-Remaining']}
39*da0073e9SAndroid Build Coastguard Worker                Resets at: {err.headers['x-RateLimit-Reset']}"""
40*da0073e9SAndroid Build Coastguard Worker            )
41*da0073e9SAndroid Build Coastguard Worker        raise
42*da0073e9SAndroid Build Coastguard Worker
43*da0073e9SAndroid Build Coastguard Worker
44*da0073e9SAndroid Build Coastguard Workerdef gh_fetch_url(
45*da0073e9SAndroid Build Coastguard Worker    url: str,
46*da0073e9SAndroid Build Coastguard Worker    *,
47*da0073e9SAndroid Build Coastguard Worker    headers: dict[str, str] | None = None,
48*da0073e9SAndroid Build Coastguard Worker    data: dict[str, Any] | None = None,
49*da0073e9SAndroid Build Coastguard Worker    method: str | None = None,
50*da0073e9SAndroid Build Coastguard Worker    reader: Callable[[Any], Any] = lambda x: x.read(),
51*da0073e9SAndroid Build Coastguard Worker) -> Any:
52*da0073e9SAndroid Build Coastguard Worker    return gh_fetch_url_and_headers(
53*da0073e9SAndroid Build Coastguard Worker        url, headers=headers, data=data, reader=json.load, method=method
54*da0073e9SAndroid Build Coastguard Worker    )[1]
55*da0073e9SAndroid Build Coastguard Worker
56*da0073e9SAndroid Build Coastguard Worker
57*da0073e9SAndroid Build Coastguard Workerdef _gh_fetch_json_any(
58*da0073e9SAndroid Build Coastguard Worker    url: str,
59*da0073e9SAndroid Build Coastguard Worker    params: dict[str, Any] | None = None,
60*da0073e9SAndroid Build Coastguard Worker    data: dict[str, Any] | None = None,
61*da0073e9SAndroid Build Coastguard Worker) -> Any:
62*da0073e9SAndroid Build Coastguard Worker    headers = {"Accept": "application/vnd.github.v3+json"}
63*da0073e9SAndroid Build Coastguard Worker    if params is not None and len(params) > 0:
64*da0073e9SAndroid Build Coastguard Worker        url += "?" + "&".join(
65*da0073e9SAndroid Build Coastguard Worker            f"{name}={quote(str(val))}" for name, val in params.items()
66*da0073e9SAndroid Build Coastguard Worker        )
67*da0073e9SAndroid Build Coastguard Worker    return gh_fetch_url(url, headers=headers, data=data, reader=json.load)
68*da0073e9SAndroid Build Coastguard Worker
69*da0073e9SAndroid Build Coastguard Worker
70*da0073e9SAndroid Build Coastguard Workerdef gh_fetch_json_dict(
71*da0073e9SAndroid Build Coastguard Worker    url: str,
72*da0073e9SAndroid Build Coastguard Worker    params: dict[str, Any] | None = None,
73*da0073e9SAndroid Build Coastguard Worker    data: dict[str, Any] | None = None,
74*da0073e9SAndroid Build Coastguard Worker) -> dict[str, Any]:
75*da0073e9SAndroid Build Coastguard Worker    return cast(Dict[str, Any], _gh_fetch_json_any(url, params, data))
76*da0073e9SAndroid Build Coastguard Worker
77*da0073e9SAndroid Build Coastguard Worker
78*da0073e9SAndroid Build Coastguard Workerdef gh_fetch_commit(org: str, repo: str, sha: str) -> dict[str, Any]:
79*da0073e9SAndroid Build Coastguard Worker    return gh_fetch_json_dict(
80*da0073e9SAndroid Build Coastguard Worker        f"https://api.github.com/repos/{org}/{repo}/commits/{sha}"
81*da0073e9SAndroid Build Coastguard Worker    )
82