diff --git a/github/MainClass.py b/github/MainClass.py index 57d329e9..2cc9f240 100644 --- a/github/MainClass.py +++ b/github/MainClass.py @@ -47,38 +47,59 @@ # along with PyGithub. If not, see . # # # ################################################################################ +from __future__ import annotations import pickle import warnings from datetime import datetime -from typing import List +from typing import TYPE_CHECKING, Any, BinaryIO, TypeVar import urllib3 +from urllib3.util import Retry import github.ApplicationOAuth +import github.Auth +import github.AuthenticatedUser import github.Enterprise import github.Event import github.Gist -import github.GithubObject +import github.GithubApp +import github.GithubIntegration +import github.GithubRetry +import github.GitignoreTemplate import github.License import github.NamedUser -import github.PaginatedList import github.Topic -from github import Auth +from github import Consts +from github.GithubIntegration import GithubIntegration +from github.GithubObject import GithubObject, NotSet, Opt, is_defined +from github.GithubRetry import GithubRetry +from github.HookDelivery import HookDelivery, HookDeliverySummary +from github.HookDescription import HookDescription +from github.PaginatedList import PaginatedList +from github.RateLimit import RateLimit +from github.Requester import Requester -from . import ( - AuthenticatedUser, - Consts, - GithubApp, - GithubRetry, - GitignoreTemplate, - HookDelivery, - HookDescription, - RateLimit, - Repository, -) -from .HookDelivery import HookDeliverySummary -from .Requester import Requester +if TYPE_CHECKING: + from github.AppAuthentication import AppAuthentication + from github.ApplicationOAuth import ApplicationOAuth + from github.AuthenticatedUser import AuthenticatedUser + from github.Commit import Commit + from github.ContentFile import ContentFile + from github.Event import Event + from github.Gist import Gist + from github.GithubApp import GithubApp + from github.GitignoreTemplate import GitignoreTemplate + from github.Issue import Issue + from github.License import License + from github.NamedUser import NamedUser + from github.Organization import Organization + from github.Project import Project + from github.ProjectColumn import ProjectColumn + from github.Repository import Repository + from github.Topic import Topic + +TGithubObject = TypeVar("TGithubObject", bound=GithubObject) class Github: @@ -86,7 +107,9 @@ class Github: This is the main class you instantiate to access the Github API v3. Optional parameters allow different authentication methods. """ - default_retry = GithubRetry.GithubRetry() + __requester: Requester + + default_retry = GithubRetry() # keep non-deprecated arguments in-sync with Requester # v3: remove login_or_token, password, jwt and app_auth @@ -95,21 +118,21 @@ class Github: # allows to reorder / add new arguments / remove deprecated arguments without breaking user code def __init__( self, - login_or_token=None, - password=None, - jwt=None, - app_auth=None, - base_url=Consts.DEFAULT_BASE_URL, - timeout=Consts.DEFAULT_TIMEOUT, - user_agent=Consts.DEFAULT_USER_AGENT, - per_page=Consts.DEFAULT_PER_PAGE, - verify=True, - retry=default_retry, - pool_size=None, - seconds_between_requests=Consts.DEFAULT_SECONDS_BETWEEN_REQUESTS, - seconds_between_writes=Consts.DEFAULT_SECONDS_BETWEEN_WRITES, - auth=None, - ): + login_or_token: str | None = None, + password: str | None = None, + jwt: str | None = None, + app_auth: AppAuthentication | None = None, + base_url: str = Consts.DEFAULT_BASE_URL, + timeout: int = Consts.DEFAULT_TIMEOUT, + user_agent: str = Consts.DEFAULT_USER_AGENT, + per_page: int = Consts.DEFAULT_PER_PAGE, + verify: bool | str = True, + retry: int | Retry | None = default_retry, + pool_size: int | None = None, + seconds_between_requests: float | None = Consts.DEFAULT_SECONDS_BETWEEN_REQUESTS, + seconds_between_writes: float | None = Consts.DEFAULT_SECONDS_BETWEEN_WRITES, + auth: github.Auth.Auth | None = None, + ) -> None: """ :param login_or_token: string deprecated, use auth=github.Auth.Login(...) or auth=github.Auth.Token(...) instead :param password: string deprecated, use auth=github.Auth.Login(...) instead @@ -141,7 +164,7 @@ class Github: assert pool_size is None or isinstance(pool_size, int), pool_size assert seconds_between_requests is None or seconds_between_requests >= 0 assert seconds_between_writes is None or seconds_between_writes >= 0 - assert auth is None or isinstance(auth, Auth.Auth), auth + assert auth is None or isinstance(auth, github.Auth.Auth), auth if password is not None: warnings.warn( @@ -149,13 +172,13 @@ class Github: "auth=github.Auth.Login(...) instead", category=DeprecationWarning, ) - auth = Auth.Login(login_or_token, password) + auth = github.Auth.Login(login_or_token, password) # type: ignore elif login_or_token is not None: warnings.warn( "Argument login_or_token is deprecated, please use " "auth=github.Auth.Token(...) instead", category=DeprecationWarning, ) - auth = Auth.Token(login_or_token) + auth = github.Auth.Token(login_or_token) elif jwt is not None: warnings.warn( "Argument jwt is deprecated, please use " @@ -163,7 +186,7 @@ class Github: "auth=github.Auth.AppAuthToken(...) instead", category=DeprecationWarning, ) - auth = Auth.AppAuthToken(jwt) + auth = github.Auth.AppAuthToken(jwt) elif app_auth is not None: warnings.warn( "Argument app_auth is deprecated, please use " "auth=github.Auth.AppInstallationAuth(...) instead", @@ -195,44 +218,36 @@ class Github: """ self.__requester.close() - def __enter__(self) -> "Github": + def __enter__(self) -> Github: return self - def __exit__(self, exc_type, exc_val, exc_tb) -> None: + def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: self.close() @property - def FIX_REPO_GET_GIT_REF(self): - """ - :type: bool - """ + def FIX_REPO_GET_GIT_REF(self) -> bool: return self.__requester.FIX_REPO_GET_GIT_REF @FIX_REPO_GET_GIT_REF.setter - def FIX_REPO_GET_GIT_REF(self, value): + def FIX_REPO_GET_GIT_REF(self, value: bool) -> None: self.__requester.FIX_REPO_GET_GIT_REF = value # v3: Remove this property? Why should it be necessary to read/modify it after construction @property - def per_page(self): - """ - :type: int - """ + def per_page(self) -> int: return self.__requester.per_page @per_page.setter - def per_page(self, value): + def per_page(self, value: int) -> None: self.__requester.per_page = value # v3: Provide a unified way to access values of headers of last response # v3: (and add/keep ad hoc properties for specific useful headers like rate limiting, oauth scopes, etc.) # v3: Return an instance of a class: using a tuple did not allow to add a field "resettime" @property - def rate_limiting(self): + def rate_limiting(self) -> tuple[int, int]: """ First value is requests remaining, second value is request limit. - - :type: (int, int) """ remaining, limit = self.__requester.rate_limiting if limit < 0: @@ -240,76 +255,67 @@ class Github: return self.__requester.rate_limiting @property - def rate_limiting_resettime(self): + def rate_limiting_resettime(self) -> int: """ Unix timestamp indicating when rate limiting will reset. - - :type: int """ if self.__requester.rate_limiting_resettime == 0: self.get_rate_limit() return self.__requester.rate_limiting_resettime - def get_rate_limit(self): + def get_rate_limit(self) -> RateLimit: """ Rate limit status for different resources (core/search/graphql). :calls: `GET /rate_limit `_ - :rtype: :class:`github.RateLimit.RateLimit` """ headers, data = self.__requester.requestJsonAndCheck("GET", "/rate_limit") - return RateLimit.RateLimit(self.__requester, headers, data["resources"], True) + return RateLimit(self.__requester, headers, data["resources"], True) @property - def oauth_scopes(self): + def oauth_scopes(self) -> list[str] | None: """ :type: list of string """ return self.__requester.oauth_scopes - def get_license(self, key=github.GithubObject.NotSet): + def get_license(self, key: Opt[str] = NotSet) -> License: """ :calls: `GET /license/{license} `_ - :param key: string - :rtype: :class:`github.License.License` """ assert isinstance(key, str), key headers, data = self.__requester.requestJsonAndCheck("GET", f"/licenses/{key}") return github.License.License(self.__requester, headers, data, completed=True) - def get_licenses(self): + def get_licenses(self) -> PaginatedList[License]: """ :calls: `GET /licenses `_ - :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.License.License` """ - url_parameters = dict() + url_parameters: dict[str, Any] = {} - return github.PaginatedList.PaginatedList(github.License.License, self.__requester, "/licenses", url_parameters) + return PaginatedList(github.License.License, self.__requester, "/licenses", url_parameters) - def get_events(self): + def get_events(self) -> PaginatedList[Event]: """ :calls: `GET /events `_ - :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Event.Event` """ - return github.PaginatedList.PaginatedList(github.Event.Event, self.__requester, "/events", None) + return PaginatedList(github.Event.Event, self.__requester, "/events", None) - def get_user(self, login=github.GithubObject.NotSet): + def get_user(self, login: Opt[str] = NotSet) -> NamedUser | AuthenticatedUser: """ :calls: `GET /users/{user} `_ or `GET /user `_ - :param login: string - :rtype: :class:`github.NamedUser.NamedUser` or :class:`github.AuthenticatedUser.AuthenticatedUser` """ - assert login is github.GithubObject.NotSet or isinstance(login, str), login - if login is github.GithubObject.NotSet: - return AuthenticatedUser.AuthenticatedUser(self.__requester, {}, {"url": "/user"}, completed=False) + assert login is NotSet or isinstance(login, str), login + if login is NotSet: + return github.AuthenticatedUser.AuthenticatedUser(self.__requester, {}, {"url": "/user"}, completed=False) else: headers, data = self.__requester.requestJsonAndCheck("GET", f"/users/{login}") return github.NamedUser.NamedUser(self.__requester, headers, data, completed=True) - def get_user_by_id(self, user_id): + def get_user_by_id(self, user_id: int) -> NamedUser: """ :calls: `GET /user/{id} `_ :param user_id: int @@ -319,48 +325,40 @@ class Github: headers, data = self.__requester.requestJsonAndCheck("GET", f"/user/{user_id}") return github.NamedUser.NamedUser(self.__requester, headers, data, completed=True) - def get_users(self, since=github.GithubObject.NotSet): + def get_users(self, since: Opt[int] = NotSet) -> PaginatedList[NamedUser]: """ :calls: `GET /users `_ - :param since: integer - :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.NamedUser.NamedUser` """ - assert since is github.GithubObject.NotSet or isinstance(since, int), since + assert since is NotSet or isinstance(since, int), since url_parameters = dict() - if since is not github.GithubObject.NotSet: + if since is not NotSet: url_parameters["since"] = since - return github.PaginatedList.PaginatedList( - github.NamedUser.NamedUser, self.__requester, "/users", url_parameters - ) + return PaginatedList(github.NamedUser.NamedUser, self.__requester, "/users", url_parameters) - def get_organization(self, login): + def get_organization(self, login: str) -> Organization: """ :calls: `GET /orgs/{org} `_ - :param login: string - :rtype: :class:`github.Organization.Organization` """ assert isinstance(login, str), login headers, data = self.__requester.requestJsonAndCheck("GET", f"/orgs/{login}") return github.Organization.Organization(self.__requester, headers, data, completed=True) - def get_organizations(self, since=github.GithubObject.NotSet): + def get_organizations(self, since: Opt[int] = NotSet) -> PaginatedList[Organization]: """ :calls: `GET /organizations `_ - :param since: integer - :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Organization.Organization` """ - assert since is github.GithubObject.NotSet or isinstance(since, int), since + assert since is NotSet or isinstance(since, int), since url_parameters = dict() - if since is not github.GithubObject.NotSet: + if since is not NotSet: url_parameters["since"] = since - return github.PaginatedList.PaginatedList( + return PaginatedList( github.Organization.Organization, self.__requester, "/organizations", url_parameters, ) - def get_enterprise(self, enterprise): + def get_enterprise(self, enterprise: str) -> github.Enterprise.Enterprise: """ :calls: `GET /enterprises/{enterprise} `_ :param enterprise: string @@ -370,58 +368,56 @@ class Github: # There is no native "/enterprises/{enterprise}" api, so this function is a hub for apis that start with "/enterprise/{enterprise}". return github.Enterprise.Enterprise(self.__requester, enterprise) - def get_repo(self, full_name_or_id, lazy=False): + def get_repo(self, full_name_or_id: int | str, lazy: bool = False) -> Repository: """ :calls: `GET /repos/{owner}/{repo} `_ or `GET /repositories/{id} `_ - :rtype: :class:`github.Repository.Repository` """ assert isinstance(full_name_or_id, (str, int)), full_name_or_id url_base = "/repositories/" if isinstance(full_name_or_id, int) else "/repos/" url = f"{url_base}{full_name_or_id}" if lazy: - return Repository.Repository(self.__requester, {}, {"url": url}, completed=False) + return github.Repository.Repository(self.__requester, {}, {"url": url}, completed=False) headers, data = self.__requester.requestJsonAndCheck("GET", url) - return Repository.Repository(self.__requester, headers, data, completed=True) + return github.Repository.Repository(self.__requester, headers, data, completed=True) - def get_repos(self, since=github.GithubObject.NotSet, visibility=github.GithubObject.NotSet): + def get_repos( + self, + since: Opt[int] = NotSet, + visibility: Opt[str] = NotSet, + ) -> PaginatedList[Repository]: """ :calls: `GET /repositories `_ :param since: integer :param visibility: string ('all','public') - :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Repository.Repository` """ - assert since is github.GithubObject.NotSet or isinstance(since, int), since - url_parameters = dict() - if since is not github.GithubObject.NotSet: + assert since is NotSet or isinstance(since, int), since + url_parameters: dict[str, Any] = {} + if since is not NotSet: url_parameters["since"] = since - if visibility is not github.GithubObject.NotSet: + if visibility is not NotSet: assert visibility in ("public", "all"), visibility url_parameters["visibility"] = visibility - return github.PaginatedList.PaginatedList( + return PaginatedList( github.Repository.Repository, self.__requester, "/repositories", url_parameters, ) - def get_project(self, id): + def get_project(self, id: int) -> Project: """ :calls: `GET /projects/{project_id} `_ - :rtype: :class:`github.Project.Project` - :param id: integer """ headers, data = self.__requester.requestJsonAndCheck( "GET", - "/projects/%d" % (id), + f"/projects/{id:d}", headers={"Accept": Consts.mediaTypeProjectsPreview}, ) return github.Project.Project(self.__requester, headers, data, completed=True) - def get_project_column(self, id): + def get_project_column(self, id: int) -> ProjectColumn: """ :calls: `GET /projects/columns/{column_id} `_ - :rtype: :class:`github.ProjectColumn.ProjectColumn` - :param id: integer """ headers, data = self.__requester.requestJsonAndCheck( "GET", @@ -430,49 +426,44 @@ class Github: ) return github.ProjectColumn.ProjectColumn(self.__requester, headers, data, completed=True) - def get_gist(self, id): + def get_gist(self, id: str) -> Gist: """ :calls: `GET /gists/{id} `_ - :param id: string - :rtype: :class:`github.Gist.Gist` """ assert isinstance(id, str), id headers, data = self.__requester.requestJsonAndCheck("GET", f"/gists/{id}") return github.Gist.Gist(self.__requester, headers, data, completed=True) - def get_gists(self, since=github.GithubObject.NotSet): + def get_gists(self, since: Opt[datetime] = NotSet) -> PaginatedList[Gist]: """ :calls: `GET /gists/public `_ - :param since: datetime format YYYY-MM-DDTHH:MM:SSZ - :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Gist.Gist` """ - assert since is github.GithubObject.NotSet or isinstance(since, datetime), since + assert since is NotSet or isinstance(since, datetime), since url_parameters = dict() - if since is not github.GithubObject.NotSet: + if is_defined(since): url_parameters["since"] = since.strftime("%Y-%m-%dT%H:%M:%SZ") - return github.PaginatedList.PaginatedList(github.Gist.Gist, self.__requester, "/gists/public", url_parameters) + return PaginatedList(github.Gist.Gist, self.__requester, "/gists/public", url_parameters) def search_repositories( self, - query, - sort=github.GithubObject.NotSet, - order=github.GithubObject.NotSet, - **qualifiers, - ): + query: str, + sort: Opt[str] = NotSet, + order: Opt[str] = NotSet, + **qualifiers: Any, + ) -> PaginatedList[Repository]: """ :calls: `GET /search/repositories `_ :param query: string :param sort: string ('stars', 'forks', 'updated') :param order: string ('asc', 'desc') :param qualifiers: keyword dict query qualifiers - :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Repository.Repository` """ assert isinstance(query, str), query url_parameters = dict() - if sort is not github.GithubObject.NotSet: # pragma no branch (Should be covered) + if sort is not NotSet: # pragma no branch (Should be covered) assert sort in ("stars", "forks", "updated"), sort url_parameters["sort"] = sort - if order is not github.GithubObject.NotSet: # pragma no branch (Should be covered) + if order is not NotSet: # pragma no branch (Should be covered) assert order in ("asc", "desc"), order url_parameters["order"] = order @@ -486,7 +477,7 @@ class Github: url_parameters["q"] = " ".join(query_chunks) assert url_parameters["q"], "need at least one qualifier" - return github.PaginatedList.PaginatedList( + return PaginatedList( github.Repository.Repository, self.__requester, "/search/repositories", @@ -495,25 +486,25 @@ class Github: def search_users( self, - query, - sort=github.GithubObject.NotSet, - order=github.GithubObject.NotSet, - **qualifiers, - ): + query: str, + sort: Opt[str] = NotSet, + order: Opt[str] = NotSet, + **qualifiers: Any, + ) -> PaginatedList[NamedUser]: """ :calls: `GET /search/users `_ :param query: string :param sort: string ('followers', 'repositories', 'joined') :param order: string ('asc', 'desc') :param qualifiers: keyword dict query qualifiers - :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.NamedUser.NamedUser` + :rtype: :class:`PaginatedList` of :class:`github.NamedUser.NamedUser` """ assert isinstance(query, str), query url_parameters = dict() - if sort is not github.GithubObject.NotSet: + if sort is not NotSet: assert sort in ("followers", "repositories", "joined"), sort url_parameters["sort"] = sort - if order is not github.GithubObject.NotSet: + if order is not NotSet: assert order in ("asc", "desc"), order url_parameters["order"] = order @@ -527,7 +518,7 @@ class Github: url_parameters["q"] = " ".join(query_chunks) assert url_parameters["q"], "need at least one qualifier" - return github.PaginatedList.PaginatedList( + return PaginatedList( github.NamedUser.NamedUser, self.__requester, "/search/users", @@ -536,25 +527,25 @@ class Github: def search_issues( self, - query, - sort=github.GithubObject.NotSet, - order=github.GithubObject.NotSet, - **qualifiers, - ): + query: str, + sort: Opt[str] = NotSet, + order: Opt[str] = NotSet, + **qualifiers: Any, + ) -> PaginatedList[Issue]: """ :calls: `GET /search/issues `_ :param query: string :param sort: string ('comments', 'created', 'updated') :param order: string ('asc', 'desc') :param qualifiers: keyword dict query qualifiers - :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Issue.Issue` + :rtype: :class:`PaginatedList` of :class:`github.Issue.Issue` """ assert isinstance(query, str), query url_parameters = dict() - if sort is not github.GithubObject.NotSet: + if sort is not NotSet: assert sort in ("comments", "created", "updated"), sort url_parameters["sort"] = sort - if order is not github.GithubObject.NotSet: + if order is not NotSet: assert order in ("asc", "desc"), order url_parameters["order"] = order @@ -568,18 +559,16 @@ class Github: url_parameters["q"] = " ".join(query_chunks) assert url_parameters["q"], "need at least one qualifier" - return github.PaginatedList.PaginatedList( - github.Issue.Issue, self.__requester, "/search/issues", url_parameters - ) + return PaginatedList(github.Issue.Issue, self.__requester, "/search/issues", url_parameters) def search_code( self, - query, - sort=github.GithubObject.NotSet, - order=github.GithubObject.NotSet, - highlight=False, - **qualifiers, - ): + query: str, + sort: Opt[str] = NotSet, + order: Opt[str] = NotSet, + highlight: bool = False, + **qualifiers: Any, + ) -> PaginatedList[ContentFile]: """ :calls: `GET /search/code `_ :param query: string @@ -587,14 +576,14 @@ class Github: :param order: string ('asc', 'desc') :param highlight: boolean (True, False) :param qualifiers: keyword dict query qualifiers - :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.ContentFile.ContentFile` + :rtype: :class:`PaginatedList` of :class:`github.ContentFile.ContentFile` """ assert isinstance(query, str), query url_parameters = dict() - if sort is not github.GithubObject.NotSet: # pragma no branch (Should be covered) + if sort is not NotSet: # pragma no branch (Should be covered) assert sort in ("indexed",), sort url_parameters["sort"] = sort - if order is not github.GithubObject.NotSet: # pragma no branch (Should be covered) + if order is not NotSet: # pragma no branch (Should be covered) assert order in ("asc", "desc"), order url_parameters["order"] = order @@ -610,7 +599,7 @@ class Github: headers = {"Accept": Consts.highLightSearchPreview} if highlight else None - return github.PaginatedList.PaginatedList( + return PaginatedList( github.ContentFile.ContentFile, self.__requester, "/search/code", @@ -620,25 +609,25 @@ class Github: def search_commits( self, - query, - sort=github.GithubObject.NotSet, - order=github.GithubObject.NotSet, - **qualifiers, - ): + query: str, + sort: Opt[str] = NotSet, + order: Opt[str] = NotSet, + **qualifiers: Any, + ) -> PaginatedList[Commit]: """ :calls: `GET /search/commits `_ :param query: string :param sort: string ('author-date', 'committer-date') :param order: string ('asc', 'desc') :param qualifiers: keyword dict query qualifiers - :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Commit.Commit` + :rtype: :class:`PaginatedList` of :class:`github.Commit.Commit` """ assert isinstance(query, str), query url_parameters = dict() - if sort is not github.GithubObject.NotSet: + if sort is not NotSet: assert sort in ("author-date", "committer-date"), sort url_parameters["sort"] = sort - if order is not github.GithubObject.NotSet: + if order is not NotSet: assert order in ("asc", "desc"), order url_parameters["order"] = order @@ -652,7 +641,7 @@ class Github: url_parameters["q"] = " ".join(query_chunks) assert url_parameters["q"], "need at least one qualifier" - return github.PaginatedList.PaginatedList( + return PaginatedList( github.Commit.Commit, self.__requester, "/search/commits", @@ -660,12 +649,12 @@ class Github: headers={"Accept": Consts.mediaTypeCommitSearchPreview}, ) - def search_topics(self, query, **qualifiers): + def search_topics(self, query: str, **qualifiers: Any) -> PaginatedList[Topic]: """ :calls: `GET /search/topics `_ :param query: string :param qualifiers: keyword dict query qualifiers - :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Topic.Topic` + :rtype: :class:`PaginatedList` of :class:`github.Topic.Topic` """ assert isinstance(query, str), query url_parameters = dict() @@ -680,7 +669,7 @@ class Github: url_parameters["q"] = " ".join(query_chunks) assert url_parameters["q"], "need at least one qualifier" - return github.PaginatedList.PaginatedList( + return PaginatedList( github.Topic.Topic, self.__requester, "/search/topics", @@ -688,7 +677,7 @@ class Github: headers={"Accept": Consts.mediaTypeTopicsPreview}, ) - def render_markdown(self, text, context=github.GithubObject.NotSet): + def render_markdown(self, text: str, context: Opt[Repository] = NotSet) -> str: """ :calls: `POST /markdown `_ :param text: string @@ -696,78 +685,68 @@ class Github: :rtype: string """ assert isinstance(text, str), text - assert context is github.GithubObject.NotSet or isinstance(context, github.Repository.Repository), context + assert context is NotSet or isinstance(context, github.Repository.Repository), context post_parameters = {"text": text} - if context is not github.GithubObject.NotSet: + if is_defined(context): post_parameters["mode"] = "gfm" post_parameters["context"] = context._identity status, headers, data = self.__requester.requestJson("POST", "/markdown", input=post_parameters) return data - def get_hook(self, name): + def get_hook(self, name: str) -> HookDescription: """ :calls: `GET /hooks/{name} `_ - :param name: string - :rtype: :class:`github.HookDescription.HookDescription` """ assert isinstance(name, str), name headers, attributes = self.__requester.requestJsonAndCheck("GET", f"/hooks/{name}") - return HookDescription.HookDescription(self.__requester, headers, attributes, completed=True) + return HookDescription(self.__requester, headers, attributes, completed=True) - def get_hooks(self): + def get_hooks(self) -> list[HookDescription]: """ :calls: `GET /hooks `_ :rtype: list of :class:`github.HookDescription.HookDescription` """ headers, data = self.__requester.requestJsonAndCheck("GET", "/hooks") - return [ - HookDescription.HookDescription(self.__requester, headers, attributes, completed=True) - for attributes in data - ] + return [HookDescription(self.__requester, headers, attributes, completed=True) for attributes in data] def get_hook_delivery(self, hook_id: int, delivery_id: int) -> HookDelivery: """ :calls: `GET /hooks/{hook_id}/deliveries/{delivery_id} `_ :param hook_id: integer :param delivery_id: integer - :rtype: :class:`github.HookDelivery.HookDelivery` + :rtype: :class:`HookDelivery` """ assert isinstance(hook_id, int), hook_id assert isinstance(delivery_id, int), delivery_id headers, attributes = self.__requester.requestJsonAndCheck("GET", f"/hooks/{hook_id}/deliveries/{delivery_id}") - return HookDelivery.HookDelivery(self.__requester, headers, attributes, completed=True) + return HookDelivery(self.__requester, headers, attributes, completed=True) - def get_hook_deliveries(self, hook_id: int) -> List[HookDeliverySummary]: + def get_hook_deliveries(self, hook_id: int) -> list[HookDeliverySummary]: """ :calls: `GET /hooks/{hook_id}/deliveries `_ :param hook_id: integer - :rtype: list of :class:`github.HookDelivery.HookDeliverySummary` + :rtype: list of :class:`HookDeliverySummary` """ assert isinstance(hook_id, int), hook_id headers, data = self.__requester.requestJsonAndCheck("GET", f"/hooks/{hook_id}/deliveries") - return [ - HookDelivery.HookDeliverySummary(self.__requester, headers, attributes, completed=True) - for attributes in data - ] + return [HookDeliverySummary(self.__requester, headers, attributes, completed=True) for attributes in data] - def get_gitignore_templates(self): + def get_gitignore_templates(self) -> list[str]: """ :calls: `GET /gitignore/templates `_ - :rtype: list of string """ headers, data = self.__requester.requestJsonAndCheck("GET", "/gitignore/templates") return data - def get_gitignore_template(self, name): + def get_gitignore_template(self, name: str) -> GitignoreTemplate: """ :calls: `GET /gitignore/templates/{name} `_ - :rtype: :class:`github.GitignoreTemplate.GitignoreTemplate` """ assert isinstance(name, str), name headers, attributes = self.__requester.requestJsonAndCheck("GET", f"/gitignore/templates/{name}") - return GitignoreTemplate.GitignoreTemplate(self.__requester, headers, attributes, completed=True) + return github.GitignoreTemplate.GitignoreTemplate(self.__requester, headers, attributes, completed=True) - def get_emojis(self): + def get_emojis(self) -> dict[str, str]: """ :calls: `GET /emojis `_ :rtype: dictionary of type => url for emoji` @@ -775,19 +754,24 @@ class Github: headers, attributes = self.__requester.requestJsonAndCheck("GET", "/emojis") return attributes - def create_from_raw_data(self, klass, raw_data, headers={}): + def create_from_raw_data( + self, klass: type[TGithubObject], raw_data: dict[str, Any], headers: dict[str, str | int] | None = None + ) -> TGithubObject: """ - Creates an object from raw_data previously obtained by :attr:`github.GithubObject.GithubObject.raw_data`, - and optionally headers previously obtained by :attr:`github.GithubObject.GithubObject.raw_headers`. + Creates an object from raw_data previously obtained by :attr:`GithubObject.raw_data`, + and optionally headers previously obtained by :attr:`GithubObject.raw_headers`. :param klass: the class of the object to create :param raw_data: dict :param headers: dict :rtype: instance of class ``klass`` """ + if headers is None: + headers = {} + return klass(self.__requester, headers, raw_data, completed=True) - def dump(self, obj, file, protocol=0): + def dump(self, obj: GithubObject, file: BinaryIO, protocol: int = 0) -> None: """ Dumps (pickles) a PyGithub object to a file-like object. Some effort is made to not pickle sensitive information like the Github credentials used in the :class:`Github` instance. @@ -799,7 +783,7 @@ class Github: """ pickle.dump((obj.__class__, obj.raw_data, obj.raw_headers), file, protocol) - def load(self, f): + def load(self, f: BinaryIO) -> Any: """ Loads (unpickles) a PyGithub object from a file-like object. @@ -808,7 +792,7 @@ class Github: """ return self.create_from_raw_data(*pickle.load(f)) - def get_oauth_application(self, client_id, client_secret): + def get_oauth_application(self, client_id: str, client_secret: str) -> ApplicationOAuth: return github.ApplicationOAuth.ApplicationOAuth( self.__requester, headers={}, @@ -816,15 +800,13 @@ class Github: completed=False, ) - def get_app(self, slug=github.GithubObject.NotSet): + def get_app(self, slug: Opt[str] = NotSet) -> GithubApp: """ :calls: `GET /apps/{slug} `_ or `GET /app `_ - :param slug: string - :rtype: :class:`github.GithubApp.GithubApp` """ - assert slug is github.GithubObject.NotSet or isinstance(slug, str), slug + assert slug is NotSet or isinstance(slug, str), slug - if slug is github.GithubObject.NotSet: + if slug is NotSet: # with no slug given, calling /app returns the authenticated app, # including the actual /apps/{slug} warnings.warn( @@ -835,8 +817,4 @@ class Github: return GithubIntegration(**self.__requester.kwargs).get_app() else: # with a slug given, we can lazily load the GithubApp - return GithubApp.GithubApp(self.__requester, {}, {"url": f"/apps/{slug}"}, completed=False) - - -# Retrocompatibility -GithubIntegration = github.GithubIntegration + return github.GithubApp.GithubApp(self.__requester, {}, {"url": f"/apps/{slug}"}, completed=False) diff --git a/github/MainClass.pyi b/github/MainClass.pyi deleted file mode 100644 index 213a9004..00000000 --- a/github/MainClass.pyi +++ /dev/null @@ -1,130 +0,0 @@ -from datetime import datetime -from io import BytesIO -from typing import Any, Dict, List, Optional, Tuple, Type, TypeVar, Union, overload - -from urllib3.util import Retry - -from github.AppAuthentication import AppAuthentication -from github.ApplicationOAuth import ApplicationOAuth -from github.Auth import Auth -from github.AuthenticatedUser import AuthenticatedUser -from github.Commit import Commit -from github.ContentFile import ContentFile -from github.Enterprise import Enterprise -from github.Event import Event -from github.Gist import Gist -from github.GithubApp import GithubApp -from github.GithubObject import GithubObject, _NotSetType -from github.GitignoreTemplate import GitignoreTemplate -from github.HookDescription import HookDescription -from github.Issue import Issue -from github.License import License -from github.NamedUser import NamedUser -from github.Organization import Organization -from github.PaginatedList import PaginatedList -from github.Project import Project -from github.ProjectColumn import ProjectColumn -from github.RateLimit import RateLimit -from github.Repository import Repository -from github.Topic import Topic - -TGithubObject = TypeVar("TGithubObject", bound=GithubObject) - -DEFAULT_BASE_URL: str -DEFAULT_STATUS_URL: str -DEFAULT_TIMEOUT: int -DEFAULT_PER_PAGE: int - -class Github: - def __init__( - self, - login_or_token: Optional[str] = ..., - password: Optional[str] = ..., - jwt: Optional[str] = ..., - app_auth: Optional[AppAuthentication] = ..., - base_url: str = ..., - timeout: int = ..., - user_agent: str = ..., - per_page: int = ..., - verify: Union[bool, str] = ..., - retry: Optional[Union[int, Retry]] = ..., - pool_size: Optional[int] = ..., - seconds_between_requests: Optional[float] = ..., - seconds_between_writes: Optional[float] = ..., - auth: Optional[Auth] = ..., - ) -> None: ... - @property - def FIX_REPO_GET_GIT_REF(self) -> bool: ... - @FIX_REPO_GET_GIT_REF.setter - def FIX_REPO_GET_GIT_REF(self, value: bool) -> None: ... - @property - def per_page(self) -> int: ... - @per_page.setter - def per_page(self, value: int) -> None: ... - def create_from_raw_data( - self, - klass: Type[TGithubObject], - raw_data: Dict[str, Any], - headers: Dict[str, Union[str, int]] = ..., - ) -> TGithubObject: ... - def dump(self, obj: GithubObject, file: BytesIO, protocol: int = ...) -> None: ... - def get_emojis(self) -> Dict[str, str]: ... - def get_events(self) -> PaginatedList[Event]: ... - def get_gist(self, id: str) -> Gist: ... - def get_gists(self, since: Union[datetime, _NotSetType] = ...) -> PaginatedList[Gist]: ... - def get_gitignore_template(self, name: str) -> GitignoreTemplate: ... - def get_gitignore_templates(self) -> List[str]: ... - def get_hook(self, name: str) -> HookDescription: ... - def get_hooks(self) -> List[HookDescription]: ... - def get_license(self, key: Union[str, _NotSetType] = ...) -> License: ... - def get_licenses(self) -> PaginatedList[License]: ... - def get_organization(self, login: str) -> Organization: ... - def get_organizations(self, since: Union[int, _NotSetType] = ...) -> PaginatedList[Organization]: ... - def get_enterprise(self, login: str) -> Enterprise: ... - def get_project(self, id: int) -> Project: ... - def get_project_column(self, id: int) -> ProjectColumn: ... - def get_rate_limit(self) -> RateLimit: ... - def get_repo(self, full_name_or_id: Union[int, str], lazy: bool = ...) -> Repository: ... - def get_repos( - self, - since: Union[int, _NotSetType] = ..., - visibility: Union[str, _NotSetType] = ..., - ) -> PaginatedList[Repository]: ... - @overload - def get_user(self, login: _NotSetType = ...) -> AuthenticatedUser: ... - @overload - def get_user(self, login: Union[str, _NotSetType] = ...) -> Union[NamedUser, AuthenticatedUser]: ... - def get_user_by_id(self, user_id: int) -> NamedUser: ... - def get_users(self, since: Union[int, _NotSetType] = ...) -> PaginatedList[NamedUser]: ... - def load(self, f: BytesIO) -> Repository: ... - # argument slug is deprecated, not included here - def get_app(self) -> GithubApp: ... - def get_oauth_application(self, client_id: str, client_secret: str) -> ApplicationOAuth: ... - @property - def oauth_scopes(self) -> Optional[List[str]]: ... - @property - def rate_limiting(self) -> Tuple[int, int]: ... - @property - def rate_limiting_resettime(self) -> int: ... - def render_markdown(self, text: str, context: Union[Repository, _NotSetType] = ...) -> str: ... - def search_code( - self, - query: str, - sort: Union[str, _NotSetType] = ..., - order: Union[str, _NotSetType] = ..., - highlight: bool = ..., - **qualifiers: Any, - ) -> PaginatedList[ContentFile]: ... - def search_commits( - self, query: str, sort: Union[str, _NotSetType] = ..., order: Union[str, _NotSetType] = ..., **qualifiers: Any - ) -> PaginatedList[Commit]: ... - def search_issues( - self, query: str, sort: Union[str, _NotSetType] = ..., order: Union[str, _NotSetType] = ..., **qualifiers: Any - ) -> PaginatedList[Issue]: ... - def search_repositories( - self, query: str, sort: Union[str, _NotSetType] = ..., order: Union[str, _NotSetType] = ..., **qualifiers: Any - ) -> PaginatedList[Repository]: ... - def search_topics(self, query: str, **qualifiers: Any) -> PaginatedList[Topic]: ... - def search_users( - self, query: str, sort: Union[str, _NotSetType] = ..., order: Union[str, _NotSetType] = ..., **qualifiers: Any - ) -> PaginatedList[NamedUser]: ... diff --git a/github/__init__.py b/github/__init__.py index d9d2ad24..ceca6297 100644 --- a/github/__init__.py +++ b/github/__init__.py @@ -35,6 +35,47 @@ like :class:`github.NamedUser.NamedUser` or :class:`github.Repository.Repository All classes inherit from :class:`github.GithubObject.GithubObject`. """ +import logging + +from . import Auth +from .AppAuthentication import AppAuthentication +from .GithubException import ( + BadAttributeException, + BadCredentialsException, + BadUserAgentException, + GithubException, + IncompletableObject, + RateLimitExceededException, + TwoFactorException, + UnknownObjectException, +) +from .GithubIntegration import GithubIntegration +from .GithubRetry import GithubRetry +from .InputFileContent import InputFileContent +from .InputGitAuthor import InputGitAuthor +from .InputGitTreeElement import InputGitTreeElement +from .MainClass import Github + +# set log level to INFO for github +logger = logging.getLogger("github") +logger.setLevel(logging.INFO) +logger.addHandler(logging.StreamHandler()) + + +def set_log_level(level: int) -> None: + """ + Set the log level of the github logger, e.g. set_log_level(logging.WARNING) + :param level: log level + """ + logger.setLevel(level) + + +def enable_console_debug_logging() -> None: # pragma no cover (Function useful only outside test environment) + """ + This function sets up a very simple logging configuration (log everything on standard output) that is useful for troubleshooting. + """ + set_log_level(logging.DEBUG) + __all__ = [ "Auth", @@ -55,45 +96,3 @@ __all__ = [ "TwoFactorException", "UnknownObjectException", ] - -import logging - -from github import Auth -from github.AppAuthentication import AppAuthentication -from github.GithubIntegration import GithubIntegration -from github.MainClass import Github - -from .GithubException import ( - BadAttributeException, - BadCredentialsException, - BadUserAgentException, - GithubException, - IncompletableObject, - RateLimitExceededException, - TwoFactorException, - UnknownObjectException, -) -from .GithubRetry import GithubRetry -from .InputFileContent import InputFileContent -from .InputGitAuthor import InputGitAuthor -from .InputGitTreeElement import InputGitTreeElement - -# set log level to INFO for github -logger = logging.getLogger("github") -logger.setLevel(logging.INFO) -logger.addHandler(logging.StreamHandler()) - - -def set_log_level(level: int): - """ - Set the log level of the github logger, e.g. set_log_level(logging.WARNING) - :param level: log level - """ - logger.setLevel(level) - - -def enable_console_debug_logging(): # pragma no cover (Function useful only outside test environment) - """ - This function sets up a very simple logging configuration (log everything on standard output) that is useful for troubleshooting. - """ - set_log_level(logging.DEBUG) diff --git a/github/__init__.pyi b/github/__init__.pyi deleted file mode 100644 index 198654c9..00000000 --- a/github/__init__.pyi +++ /dev/null @@ -1,19 +0,0 @@ -from github import Auth as Auth -from github.AppAuthentication import AppAuthentication as AppAuthentication -from github.GithubIntegration import GithubIntegration as GithubIntegration -from github.GithubRetry import GithubRetry as GithubRetry -from github.MainClass import Github as Github - -from .GithubException import BadAttributeException as BadAttributeException -from .GithubException import BadCredentialsException as BadCredentialsException -from .GithubException import BadUserAgentException as BadUserAgentException -from .GithubException import GithubException as GithubException -from .GithubException import IncompletableObject as IncompletableObject -from .GithubException import RateLimitExceededException as RateLimitExceededException -from .GithubException import TwoFactorException as TwoFactorException -from .GithubException import UnknownObjectException as UnknownObjectException -from .InputFileContent import InputFileContent as InputFileContent -from .InputGitAuthor import InputGitAuthor as InputGitAuthor -from .InputGitTreeElement import InputGitTreeElement as InputGitTreeElement - -def enable_console_debug_logging() -> None: ...