Skip to content

Commit 9af9b6e

Browse files
authored
Add missing GitHub classes to docs (#2783)
1 parent 64b1cde commit 9af9b6e

File tree

8 files changed

+97
-51
lines changed

8 files changed

+97
-51
lines changed

.readthedocs.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@
55
# Required
66
version: 2
77

8+
build:
9+
os: ubuntu-22.04
10+
tools:
11+
python: "3.8"
12+
813
sphinx:
914
configuration: doc/conf.py
1015

1116
python:
12-
version: 3.8
1317
install:
1418
- method: pip
1519
path: .
1620
- requirements: requirements.txt
21+
- requirements: requirements/docs.txt

doc/conf.py

Lines changed: 54 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@
2525
# along with PyGithub. If not, see <http://www.gnu.org/licenses/>. #
2626
# #
2727
################################################################################
28+
from __future__ import annotations
2829

2930
import datetime
3031
import glob
3132
import os
33+
import re
3234
import sys
35+
from typing import Iterable
3336

3437
# If extensions (or modules to document with autodoc) are in another directory,
3538
# add these directories to sys.path here. If the directory is relative to the
@@ -268,24 +271,45 @@
268271
autodoc_member_order = "bysource"
269272
autoclass_content = "both"
270273

271-
githubClasses = [
272-
fileName[10:-3]
273-
for fileName in sorted(glob.glob("../github/*.py"))
274-
if fileName
275-
not in [
276-
"../github/GithubException.py",
277-
"../github/GithubObject.py",
278-
"../github/InputFileContent.py",
279-
"../github/InputGitAuthor.py",
280-
"../github/InputGitTreeElement.py",
281-
"../github/Legacy.py",
282-
"../github/MainClass.py",
283-
"../github/PaginatedList.py",
284-
"../github/Requester.py",
285-
"../github/Consts.py",
286-
"../github/__init__.py",
287-
]
288-
]
274+
githubObjectTypes = {
275+
variation
276+
for object_type in ["GithubObject", "CompletableGithubObject", "NonCompletableGithubObject"]
277+
for variation in [object_type, "GithubObject." + object_type, "github.GithubObject." + object_type]
278+
}
279+
githubObjectClasses: dict[str, str] = {}
280+
281+
282+
def collect_classes(types: set[str]) -> Iterable[tuple[str, str]]:
283+
def get_base_classes(class_definition: str) -> Iterable[str]:
284+
if "(" in class_definition and ")" in class_definition:
285+
for base in class_definition[class_definition.index("(") + 1 : class_definition.index(")")].split(","):
286+
yield base.strip()
287+
else:
288+
return []
289+
290+
for filename in sorted(glob.glob("../github/*.py")):
291+
module = f"github.{filename[10:-3]}"
292+
with open(filename) as r:
293+
for line in r.readlines():
294+
if line.startswith("class ") and any([base in types for base in get_base_classes(line)]):
295+
class_name = re.match(r"class (\w+)[:(]", line).group(1)
296+
if class_name not in types:
297+
yield class_name, f"{module}"
298+
299+
300+
# get all classes derived from GithubObject classes directly
301+
classes = list(collect_classes(githubObjectTypes))
302+
while classes:
303+
githubObjectClasses.update(classes)
304+
# get all classes derived from detected classes
305+
githubObjectTypes.update(
306+
{
307+
variation
308+
for object_type in [cls for cls, _ in classes]
309+
for variation in [object_type, "GithubObject." + object_type, "github.GithubObject." + object_type]
310+
}
311+
)
312+
classes = list(collect_classes(set(githubObjectTypes)))
289313

290314
with open("github_objects.rst", "w") as f:
291315
f.write("Github objects\n")
@@ -294,23 +318,21 @@
294318
f.write(".. autoclass:: github.GithubObject.GithubObject()\n")
295319
f.write("\n")
296320
f.write(".. toctree::\n")
297-
for githubClass in githubClasses:
298-
f.write(" github_objects/" + githubClass + "\n")
321+
for githubObjectClass in sorted(githubObjectClasses.keys()):
322+
f.write(" github_objects/" + githubObjectClass + "\n")
299323

300-
for githubClass in githubClasses:
301-
with open("github_objects/" + githubClass + ".rst", "w") as f:
302-
f.write(githubClass + "\n")
303-
f.write("=" * len(githubClass) + "\n")
324+
for githubObjectClass, module in githubObjectClasses.items():
325+
with open("github_objects/" + githubObjectClass + ".rst", "w") as f:
326+
f.write(githubObjectClass + "\n")
327+
f.write("=" * len(githubObjectClass) + "\n")
304328
f.write("\n")
305-
f.write(".. autoclass:: github." + githubClass + "." + githubClass + "()\n")
329+
f.write(".. autoclass:: " + module + "." + githubObjectClass + "()\n")
306330

307331
methods = dict()
308-
for githubClass in githubClasses + ["MainClass"]:
309-
with open("../github/" + githubClass + ".py") as f:
310-
if githubClass == "MainClass":
311-
githubClass = "github.MainClass.Github"
312-
else:
313-
githubClass = "github." + githubClass + "." + githubClass
332+
githubObjectClasses.update([("MainClass", "github.MainClass")])
333+
githubObjectClasses.update([("GithubIntegration", "github.GithubIntegration")])
334+
for githubObjectClass, module in githubObjectClasses.items():
335+
with open("../" + module.replace(".", "/") + ".py") as f:
314336
method = None
315337
isProperty = False
316338
for line in f:
@@ -345,7 +367,7 @@
345367
methods[url] = dict()
346368
if verb not in methods[url]:
347369
methods[url][verb] = set()
348-
methods[url][verb].add(":meth:`" + githubClass + "." + method + "`")
370+
methods[url][verb].add(":meth:`" + module + "." + githubObjectClass + "." + method + "`")
349371
method = None
350372

351373
methods["/markdown/raw"] = dict()

doc/examples/MainClass.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Get organization by name
4040
u'PyGithub'
4141
4242
Get enterprise consumed licenses by name
43-
------------------------
43+
----------------------------------------
4444

4545
.. code-block:: python
4646

doc/github_integration.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Main class: GithubIntegration
2+
=============================
3+
4+
.. autoclass:: github.GithubIntegration.GithubIntegration

doc/reference.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Reference
66

77
.. toctree::
88
github
9+
github_integration
910
apis
1011
utilities
1112
github_objects

doc/utilities.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
Utilities
22
=========
33

4+
Authentication
5+
--------------
6+
7+
.. autoclass:: github.Auth.Login
8+
.. autoclass:: github.Auth.Token
9+
.. autoclass:: github.Auth.JWT
10+
.. autoclass:: github.Auth.AppAuth
11+
.. autoclass:: github.Auth.AppAuthToken
12+
.. autoclass:: github.Auth.AppInstallationAuth
13+
.. autoclass:: github.Auth.AppUserAuth
14+
.. autoclass:: github.Auth.NetrcAuth
15+
416
Logging
517
-------
618

github/Auth.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import time
2626
from abc import ABC
2727
from datetime import datetime, timedelta, timezone
28-
from typing import Dict, Optional, Union
28+
from typing import TYPE_CHECKING, Dict, Optional, Union
2929

3030
import jwt
3131
from requests import utils
@@ -34,11 +34,15 @@
3434
from github.InstallationAuthorization import InstallationAuthorization
3535
from github.Requester import Requester, WithRequester
3636

37+
if TYPE_CHECKING:
38+
from github.GithubIntegration import GithubIntegration
39+
3740
# For App authentication, time remaining before token expiration to request a new one
3841
ACCESS_TOKEN_REFRESH_THRESHOLD_SECONDS = 20
3942
TOKEN_REFRESH_THRESHOLD_TIMEDELTA = timedelta(seconds=ACCESS_TOKEN_REFRESH_THRESHOLD_SECONDS)
4043

4144

45+
# add new implementations of github.Auth.Auth to docs/utilities.rst
4246
class Auth(abc.ABC):
4347
"""
4448
This class is the base class of all authentication methods for Requester.
@@ -83,7 +87,7 @@ def token(self) -> str:
8387

8488
class Login(HTTPBasicAuth):
8589
"""
86-
This class is used to authenticate Requester with login and password.
90+
This class is used to authenticate with login and password.
8791
"""
8892

8993
def __init__(self, login: str, password: str):
@@ -110,7 +114,7 @@ def password(self) -> str:
110114

111115
class Token(Auth):
112116
"""
113-
This class is used to authenticate Requester with a single constant token.
117+
This class is used to authenticate with a single constant token.
114118
"""
115119

116120
def __init__(self, token: str):
@@ -140,7 +144,7 @@ def token_type(self) -> str:
140144

141145
class AppAuth(JWT):
142146
"""
143-
This class is used to authenticate Requester as a GitHub App.
147+
This class is used to authenticate as a GitHub App.
144148
https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app
145149
"""
146150

@@ -219,7 +223,7 @@ def create_jwt(self, expiration: Optional[int] = None) -> str:
219223

220224
class AppAuthToken(JWT):
221225
"""
222-
This class is used to authenticate Requester as a GitHub App with a single constant JWT.
226+
This class is used to authenticate as a GitHub App with a single constant JWT.
223227
https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app
224228
"""
225229

@@ -235,15 +239,12 @@ def token(self) -> str:
235239

236240
class AppInstallationAuth(Auth, WithRequester["AppInstallationAuth"]):
237241
"""
238-
This class is used to authenticate Requester as a GitHub App Installation.
242+
This class is used to authenticate as a GitHub App Installation.
239243
https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation
240244
"""
241245

242-
# imported here to avoid circular import, needed for typing only
243-
from github.GithubIntegration import GithubIntegration
244-
245246
# used to fetch live access token when calling self.token
246-
__integration: Optional[GithubIntegration] = None
247+
__integration: Optional["GithubIntegration"] = None
247248
__installation_authorization: Optional[InstallationAuthorization] = None
248249

249250
def __init__(
@@ -269,6 +270,7 @@ def __init__(
269270
def withRequester(self, requester: Requester) -> "AppInstallationAuth":
270271
super().withRequester(requester.withAuth(self._app_auth))
271272

273+
# imported here to avoid circular import
272274
from github.GithubIntegration import GithubIntegration
273275

274276
self.__integration = GithubIntegration(**self.requester.kwargs)
@@ -317,7 +319,7 @@ def _get_installation_authorization(self) -> InstallationAuthorization:
317319

318320
class AppUserAuth(Auth, WithRequester["AppUserAuth"]):
319321
"""
320-
This class is used to authenticate Requester as a GitHub App on behalf of a user.
322+
This class is used to authenticate as a GitHub App on behalf of a user.
321323
https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-with-a-github-app-on-behalf-of-a-user
322324
"""
323325

@@ -445,7 +447,7 @@ def refresh_expires_at(self) -> Optional[datetime]:
445447

446448
class NetrcAuth(HTTPBasicAuth, WithRequester["NetrcAuth"]):
447449
"""
448-
This class is used to authenticate Requester via .netrc.
450+
This class is used to authenticate via .netrc.
449451
"""
450452

451453
def __init__(self) -> None:

github/RepositoryAdvisory.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,9 @@ def offer_credit(
199199
credit_type: str,
200200
) -> None:
201201
"""
202-
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
203202
Offers credit to a user for a vulnerability in a repository.
204203
Unless you are giving credit to yourself, the user having credit offered will need to explicitly accept the credit.
204+
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
205205
"""
206206
self.offer_credits([{"login": login_or_user, "type": credit_type}])
207207

@@ -210,9 +210,9 @@ def offer_credits(
210210
credited: Iterable[Credit],
211211
) -> None:
212212
"""
213-
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
214213
Offers credit to a list of users for a vulnerability in a repository.
215214
Unless you are giving credit to yourself, the user having credit offered will need to explicitly accept the credit.
215+
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
216216
:param credited: iterable of dict with keys "login" and "type"
217217
"""
218218
assert isinstance(credited, Iterable), credited
@@ -328,8 +328,8 @@ def edit(
328328

329329
def accept_report(self) -> None:
330330
"""
331-
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
332331
Accepts the advisory reported from an external reporter via private vulnerability reporting.
332+
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
333333
"""
334334
patch_parameters = {"state": "draft"}
335335
headers, data = self._requester.requestJsonAndCheck(
@@ -341,8 +341,8 @@ def accept_report(self) -> None:
341341

342342
def publish(self) -> None:
343343
"""
344-
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
345344
Publishes the advisory.
345+
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
346346
"""
347347
patch_parameters = {"state": "published"}
348348
headers, data = self._requester.requestJsonAndCheck(
@@ -354,8 +354,8 @@ def publish(self) -> None:
354354

355355
def close(self) -> None:
356356
"""
357-
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
358357
Closes the advisory.
358+
:calls: `PATCH /repos/{owner}/{repo}/security-advisories/:advisory_id <https://docs.github.com/en/rest/security-advisories/repository-advisories>`
359359
"""
360360
patch_parameters = {"state": "closed"}
361361
headers, data = self._requester.requestJsonAndCheck(

0 commit comments

Comments
 (0)