mirror of
https://github.com/RGBCube/GitHubWrapper
synced 2025-05-19 07:25:09 +00:00
refactor w/ black
This commit is contained in:
parent
cc3cde89c8
commit
b474492637
8 changed files with 187 additions and 165 deletions
|
@ -6,9 +6,7 @@ from collections import deque
|
||||||
from collections.abc import MutableMapping
|
from collections.abc import MutableMapping
|
||||||
from typing import Any, Deque, Tuple, TypeVar
|
from typing import Any, Deque, Tuple, TypeVar
|
||||||
|
|
||||||
__all__: Tuple[str, ...] = (
|
__all__: Tuple[str, ...] = ('ObjectCache',)
|
||||||
'ObjectCache',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
K = TypeVar('K')
|
K = TypeVar('K')
|
||||||
|
@ -50,6 +48,7 @@ class _BaseCache(MutableMapping[K, V]):
|
||||||
|
|
||||||
class ObjectCache(_BaseCache[K, V]):
|
class ObjectCache(_BaseCache[K, V]):
|
||||||
"""This adjusts the typehints to reflect Github objects."""
|
"""This adjusts the typehints to reflect Github objects."""
|
||||||
|
|
||||||
def __getitem__(self, __k: K) -> V:
|
def __getitem__(self, __k: K) -> V:
|
||||||
index = self._lru_keys.index(__k)
|
index = self._lru_keys.index(__k)
|
||||||
target = self._lru_keys[index]
|
target = self._lru_keys[index]
|
||||||
|
|
|
@ -26,106 +26,139 @@ __all__ = (
|
||||||
'RepositoryAlreadyExists',
|
'RepositoryAlreadyExists',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class APIError(Exception):
|
class APIError(Exception):
|
||||||
"""Base level exceptions raised by errors related to any API request or call."""
|
"""Base level exceptions raised by errors related to any API request or call."""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class HTTPException(Exception):
|
class HTTPException(Exception):
|
||||||
"""Base level exceptions raised by errors related to HTTP requests."""
|
"""Base level exceptions raised by errors related to HTTP requests."""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ClientException(Exception):
|
class ClientException(Exception):
|
||||||
"""Base level exceptions raised by errors related to the client."""
|
"""Base level exceptions raised by errors related to the client."""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ResourceNotFound(Exception):
|
class ResourceNotFound(Exception):
|
||||||
"""Base level exceptions raised when a resource is not found."""
|
"""Base level exceptions raised when a resource is not found."""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ResourceAlreadyExists(Exception):
|
class ResourceAlreadyExists(Exception):
|
||||||
"""Base level exceptions raised when a resource already exists."""
|
"""Base level exceptions raised when a resource already exists."""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Ratelimited(APIError):
|
class Ratelimited(APIError):
|
||||||
"""Raised when the ratelimit from Github is reached or exceeded."""
|
"""Raised when the ratelimit from Github is reached or exceeded."""
|
||||||
|
|
||||||
def __init__(self, reset_time: datetime.datetime):
|
def __init__(self, reset_time: datetime.datetime):
|
||||||
formatted = reset_time.strftime(r"%H:%M:%S %A, %d %b")
|
formatted = reset_time.strftime(r"%H:%M:%S %A, %d %b")
|
||||||
msg = "We're being ratelimited, wait until {}.\nAuthentication raises the ratelimit.".format(formatted)
|
msg = "We're being ratelimited, wait until {}.\nAuthentication raises the ratelimit.".format(formatted)
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class WillExceedRatelimit(APIError):
|
class WillExceedRatelimit(APIError):
|
||||||
"""Raised when the library predicts the call will exceed the ratelimit, will abort the call by default."""
|
"""Raised when the library predicts the call will exceed the ratelimit, will abort the call by default."""
|
||||||
|
|
||||||
def __init__(self, response: ClientRequest, count: int):
|
def __init__(self, response: ClientRequest, count: int):
|
||||||
msg = 'Performing this action will exceed the ratelimit, aborting.\n{} remaining available calls, calls to make: {}.'
|
msg = 'Performing this action will exceed the ratelimit, aborting.\n{} remaining available calls, calls to make: {}.'
|
||||||
msg = msg.format(response.headers['X-RateLimit-Remaining'], count)
|
msg = msg.format(response.headers['X-RateLimit-Remaining'], count)
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class NoAuthProvided(ClientException):
|
class NoAuthProvided(ClientException):
|
||||||
"""Raised when no authentication is provided."""
|
"""Raised when no authentication is provided."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
msg = 'This action required autentication. Pass username and token kwargs to your client instance.'
|
msg = 'This action required autentication. Pass username and token kwargs to your client instance.'
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class InvalidToken(ClientException):
|
class InvalidToken(ClientException):
|
||||||
"""Raised when the token provided is invalid."""
|
"""Raised when the token provided is invalid."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
msg = 'The token provided is invalid.'
|
msg = 'The token provided is invalid.'
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class InvalidAuthCombination(ClientException):
|
class InvalidAuthCombination(ClientException):
|
||||||
"""Raised when the username and token are both provided."""
|
"""Raised when the username and token are both provided."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
msg = 'The username and token cannot be used together.'
|
msg = 'The username and token cannot be used together.'
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class LoginFailure(ClientException):
|
class LoginFailure(ClientException):
|
||||||
"""Raised when the login attempt fails."""
|
"""Raised when the login attempt fails."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
msg = 'The login attempt failed. Provide valid credentials.'
|
msg = 'The login attempt failed. Provide valid credentials.'
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class NotStarted(ClientException):
|
class NotStarted(ClientException):
|
||||||
"""Raised when the client is not started."""
|
"""Raised when the client is not started."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
msg = 'The client is not started. Run Github.GHClient() to start.'
|
msg = 'The client is not started. Run Github.GHClient() to start.'
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class AlreadyStarted(ClientException):
|
class AlreadyStarted(ClientException):
|
||||||
"""Raised when the client is already started."""
|
"""Raised when the client is already started."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
msg = 'The client is already started.'
|
msg = 'The client is already started.'
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class MissingPermissions(APIError):
|
class MissingPermissions(APIError):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
msg = 'You do not have permissions to perform this action.'
|
msg = 'You do not have permissions to perform this action.'
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class UserNotFound(ResourceNotFound):
|
class UserNotFound(ResourceNotFound):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
msg = 'The requested user was not found.'
|
msg = 'The requested user was not found.'
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class RepositoryNotFound(ResourceNotFound):
|
class RepositoryNotFound(ResourceNotFound):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
msg = 'The requested repository is either private or does not exist.'
|
msg = 'The requested repository is either private or does not exist.'
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class IssueNotFound(ResourceNotFound):
|
class IssueNotFound(ResourceNotFound):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
msg = 'The requested issue was not found.'
|
msg = 'The requested issue was not found.'
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class OrganizationNotFound(ResourceNotFound):
|
class OrganizationNotFound(ResourceNotFound):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
msg = 'The requested organization was not found.'
|
msg = 'The requested organization was not found.'
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class GistNotFound(ResourceNotFound):
|
class GistNotFound(ResourceNotFound):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
msg = 'The requested gist was not found.'
|
msg = 'The requested gist was not found.'
|
||||||
super().__init__(msg)
|
super().__init__(msg)
|
||||||
|
|
||||||
|
|
||||||
class RepositoryAlreadyExists(ResourceAlreadyExists):
|
class RepositoryAlreadyExists(ResourceAlreadyExists):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
msg = 'The requested repository already exists.'
|
msg = 'The requested repository already exists.'
|
||||||
|
|
|
@ -29,19 +29,14 @@ Rates = NamedTuple('Rates', 'remaining', 'used', 'total', 'reset_when', 'last_re
|
||||||
|
|
||||||
# aiohttp request tracking / checking bits
|
# aiohttp request tracking / checking bits
|
||||||
async def on_req_start(
|
async def on_req_start(
|
||||||
session: aiohttp.ClientSession,
|
session: aiohttp.ClientSession, ctx: SimpleNamespace, params: aiohttp.TraceRequestStartParams
|
||||||
ctx: SimpleNamespace,
|
|
||||||
params: aiohttp.TraceRequestStartParams
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Before-request hook to make sure we don't overrun the ratelimit."""
|
"""Before-request hook to make sure we don't overrun the ratelimit."""
|
||||||
# print(repr(session), repr(ctx), repr(params))
|
# print(repr(session), repr(ctx), repr(params))
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def on_req_end(
|
|
||||||
session: aiohttp.ClientSession,
|
async def on_req_end(session: aiohttp.ClientSession, ctx: SimpleNamespace, params: aiohttp.TraceRequestEndParams) -> None:
|
||||||
ctx: SimpleNamespace,
|
|
||||||
params: aiohttp.TraceRequestEndParams
|
|
||||||
) -> None:
|
|
||||||
"""After-request hook to adjust remaining requests on this time frame."""
|
"""After-request hook to adjust remaining requests on this time frame."""
|
||||||
headers = params.response.headers
|
headers = params.response.headers
|
||||||
|
|
||||||
|
@ -53,28 +48,30 @@ async def on_req_end(
|
||||||
|
|
||||||
session._rates = Rates(remaining, used, total, reset_when, last_req)
|
session._rates = Rates(remaining, used, total, reset_when, last_req)
|
||||||
|
|
||||||
|
|
||||||
trace_config = aiohttp.TraceConfig()
|
trace_config = aiohttp.TraceConfig()
|
||||||
trace_config.on_request_start.append(on_req_start)
|
trace_config.on_request_start.append(on_req_start)
|
||||||
trace_config.on_request_end.append(on_req_end)
|
trace_config.on_request_end.append(on_req_end)
|
||||||
|
|
||||||
APIType: TypeAlias = Union[User, Gist, Repository]
|
APIType: TypeAlias = Union[User, Gist, Repository]
|
||||||
|
|
||||||
|
|
||||||
async def make_session(*, headers: Dict[str, str], authorization: Union[aiohttp.BasicAuth, None]) -> aiohttp.ClientSession:
|
async def make_session(*, headers: Dict[str, str], authorization: Union[aiohttp.BasicAuth, None]) -> aiohttp.ClientSession:
|
||||||
"""This makes the ClientSession, attaching the trace config and ensuring a UA header is present."""
|
"""This makes the ClientSession, attaching the trace config and ensuring a UA header is present."""
|
||||||
if not headers.get('User-Agent'):
|
if not headers.get('User-Agent'):
|
||||||
headers['User-Agent'] = f'Github-API-Wrapper (https://github.com/VarMonke/Github-Api-Wrapper) @ {__version__} Python {platform.python_version()} aiohttp {aiohttp.__version__}'
|
headers[
|
||||||
|
'User-Agent'
|
||||||
|
] = f'Github-API-Wrapper (https://github.com/VarMonke/Github-Api-Wrapper) @ {__version__} Python {platform.python_version()} aiohttp {aiohttp.__version__}'
|
||||||
|
|
||||||
session = aiohttp.ClientSession(
|
session = aiohttp.ClientSession(auth=authorization, headers=headers, trace_configs=[trace_config])
|
||||||
auth=authorization,
|
|
||||||
headers=headers,
|
|
||||||
trace_configs=[trace_config]
|
|
||||||
)
|
|
||||||
session._rates = Rates('', '', '', '', '')
|
session._rates = Rates('', '', '', '', '')
|
||||||
return session
|
return session
|
||||||
|
|
||||||
|
|
||||||
# pagination
|
# pagination
|
||||||
class Paginator:
|
class Paginator:
|
||||||
"""This class handles pagination for objects like Repos and Orgs."""
|
"""This class handles pagination for objects like Repos and Orgs."""
|
||||||
|
|
||||||
def __init__(self, session: aiohttp.ClientSession, response: aiohttp.ClientResponse, target_type: str):
|
def __init__(self, session: aiohttp.ClientSession, response: aiohttp.ClientResponse, target_type: str):
|
||||||
self.session = session
|
self.session = session
|
||||||
self.response = response
|
self.response = response
|
||||||
|
@ -82,7 +79,7 @@ class Paginator:
|
||||||
types: Dict[str, Type[APIType]] = { # note: the type checker doesnt see subclasses like that
|
types: Dict[str, Type[APIType]] = { # note: the type checker doesnt see subclasses like that
|
||||||
'user': User,
|
'user': User,
|
||||||
'gist': Gist,
|
'gist': Gist,
|
||||||
'repo' : Repository
|
'repo': Repository,
|
||||||
}
|
}
|
||||||
self.target_type: Type[APIType] = types[target_type]
|
self.target_type: Type[APIType] = types[target_type]
|
||||||
self.pages = {}
|
self.pages = {}
|
||||||
|
@ -121,13 +118,17 @@ class Paginator:
|
||||||
raise WillExceedRatelimit(response, self.max_page)
|
raise WillExceedRatelimit(response, self.max_page)
|
||||||
self.bare_link = groups[0][0][:-1]
|
self.bare_link = groups[0][0][:-1]
|
||||||
|
|
||||||
|
|
||||||
# GithubUserData = GithubRepoData = GithubIssueData = GithubOrgData = GithubGistData = Dict[str, Union [str, int]]
|
# GithubUserData = GithubRepoData = GithubIssueData = GithubOrgData = GithubGistData = Dict[str, Union [str, int]]
|
||||||
# Commentnig this out for now, consider using TypeDict's instead in the future <3
|
# Commentnig this out for now, consider using TypeDict's instead in the future <3
|
||||||
|
|
||||||
|
|
||||||
class http:
|
class http:
|
||||||
def __init__(self, headers: Dict[str, Union[str, int]], auth: Union[aiohttp.BasicAuth, None]) -> None:
|
def __init__(self, headers: Dict[str, Union[str, int]], auth: Union[aiohttp.BasicAuth, None]) -> None:
|
||||||
if not headers.get('User-Agent'):
|
if not headers.get('User-Agent'):
|
||||||
headers['User-Agent'] = f'Github-API-Wrapper (https://github.com/VarMonke/Github-Api-Wrapper) @ {__version__} Python/{platform.python_version()} aiohttp/{aiohttp.__version__}'
|
headers[
|
||||||
|
'User-Agent'
|
||||||
|
] = f'Github-API-Wrapper (https://github.com/VarMonke/Github-Api-Wrapper) @ {__version__} Python/{platform.python_version()} aiohttp/{aiohttp.__version__}'
|
||||||
|
|
||||||
self._rates = Rates('', '', '', '', '')
|
self._rates = Rates('', '', '', '', '')
|
||||||
self.headers = headers
|
self.headers = headers
|
||||||
|
@ -149,6 +150,7 @@ class http:
|
||||||
def update_headers(self, *, flush: bool = False, new_headers: Dict[str, Union[str, int]]):
|
def update_headers(self, *, flush: bool = False, new_headers: Dict[str, Union[str, int]]):
|
||||||
if flush:
|
if flush:
|
||||||
from multidict import CIMultiDict
|
from multidict import CIMultiDict
|
||||||
|
|
||||||
self.session._default_headers = CIMultiDict(**new_headers) # type: ignore
|
self.session._default_headers = CIMultiDict(**new_headers) # type: ignore
|
||||||
else:
|
else:
|
||||||
self.session._default_headers = {**self.session.headers, **new_headers} # type: ignore
|
self.session._default_headers = {**self.session.headers, **new_headers} # type: ignore
|
||||||
|
@ -158,11 +160,7 @@ class http:
|
||||||
headers = self.session.headers
|
headers = self.session.headers
|
||||||
config = self.session.trace_configs
|
config = self.session.trace_configs
|
||||||
await self.session.close()
|
await self.session.close()
|
||||||
self.session = aiohttp.ClientSession(
|
self.session = aiohttp.ClientSession(headers=headers, auth=auth, trace_configs=config)
|
||||||
headers=headers,
|
|
||||||
auth=auth,
|
|
||||||
trace_configs=config
|
|
||||||
)
|
|
||||||
|
|
||||||
def data(self):
|
def data(self):
|
||||||
# return session headers and auth
|
# return session headers and auth
|
||||||
|
@ -260,29 +258,26 @@ class http:
|
||||||
raise GistNotFound
|
raise GistNotFound
|
||||||
|
|
||||||
async def create_gist(
|
async def create_gist(
|
||||||
self,
|
self, *, files: List['File'] = [], description: str = 'Default description', public: bool = False
|
||||||
*,
|
|
||||||
files: List['File'] = [],
|
|
||||||
description: str = 'Default description',
|
|
||||||
public: bool = False
|
|
||||||
) -> Dict[str, Union[str, int]]:
|
) -> Dict[str, Union[str, int]]:
|
||||||
data = {}
|
data = {}
|
||||||
data['description'] = description
|
data['description'] = description
|
||||||
data['public'] = public
|
data['public'] = public
|
||||||
data['files'] = {}
|
data['files'] = {}
|
||||||
for file in files:
|
for file in files:
|
||||||
data['files'][file.filename] = {
|
data['files'][file.filename] = {'filename': file.filename, 'content': file.read()} # helps editing the file
|
||||||
'filename' : file.filename, # helps editing the file
|
|
||||||
'content': file.read()
|
|
||||||
}
|
|
||||||
data = json.dumps(data)
|
data = json.dumps(data)
|
||||||
_headers = dict(self.session.headers)
|
_headers = dict(self.session.headers)
|
||||||
result = await self.session.post(CREATE_GIST_URL, data=data, headers=_headers|{'Accept': 'application/vnd.github.v3+json'})
|
result = await self.session.post(
|
||||||
|
CREATE_GIST_URL, data=data, headers=_headers | {'Accept': 'application/vnd.github.v3+json'}
|
||||||
|
)
|
||||||
if 201 == result.status:
|
if 201 == result.status:
|
||||||
return await result.json()
|
return await result.json()
|
||||||
raise InvalidToken
|
raise InvalidToken
|
||||||
|
|
||||||
async def create_repo(self, name: str, description: str, public: bool, gitignore: Optional[str], license: Optional[str]) -> Dict[str, Union [str, int]]:
|
async def create_repo(
|
||||||
|
self, name: str, description: str, public: bool, gitignore: Optional[str], license: Optional[str]
|
||||||
|
) -> Dict[str, Union[str, int]]:
|
||||||
"""Creates a repo for you with given data"""
|
"""Creates a repo for you with given data"""
|
||||||
data = {
|
data = {
|
||||||
'name': name,
|
'name': name,
|
||||||
|
|
|
@ -74,9 +74,7 @@ class GHClient:
|
||||||
return f'<{self.__class__.__name__} has_auth={bool(self._auth)}>'
|
return f'<{self.__class__.__name__} has_auth={bool(self._auth)}>'
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
asyncio.create_task(
|
asyncio.create_task(self.http.session.close(), name='cleanup-session-github-api-wrapper')
|
||||||
self.http.session.close(), name='cleanup-session-github-api-wrapper'
|
|
||||||
)
|
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
def check_limits(self, as_dict: Literal[True] = True) -> Dict[str, Union[str, int]]:
|
def check_limits(self, as_dict: Literal[True] = True) -> Dict[str, Union[str, int]]:
|
||||||
|
@ -86,9 +84,7 @@ class GHClient:
|
||||||
def check_limits(self, as_dict: Literal[False] = False) -> List[str]:
|
def check_limits(self, as_dict: Literal[False] = False) -> List[str]:
|
||||||
...
|
...
|
||||||
|
|
||||||
def check_limits(
|
def check_limits(self, as_dict: bool = False) -> Union[Dict[str, Union[str, int]], List[str]]:
|
||||||
self, as_dict: bool = False
|
|
||||||
) -> Union[Dict[str, Union[str, int]], List[str]]:
|
|
||||||
if not self.has_started:
|
if not self.has_started:
|
||||||
raise exceptions.NotStarted
|
raise exceptions.NotStarted
|
||||||
if not as_dict:
|
if not as_dict:
|
||||||
|
@ -132,13 +128,9 @@ class GHClient:
|
||||||
]:
|
]:
|
||||||
def wrapper(
|
def wrapper(
|
||||||
func: Callable[Concatenate[Self, P], Awaitable[T]]
|
func: Callable[Concatenate[Self, P], Awaitable[T]]
|
||||||
) -> Callable[
|
) -> Callable[Concatenate[Self, P], Awaitable[Optional[Union[T, User, Repository]]]]:
|
||||||
Concatenate[Self, P], Awaitable[Optional[Union[T, User, Repository]]]
|
|
||||||
]:
|
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
async def wrapped(
|
async def wrapped(self: Self, *args: P.args, **kwargs: P.kwargs) -> Optional[Union[T, User, Repository]]:
|
||||||
self: Self, *args: P.args, **kwargs: P.kwargs
|
|
||||||
) -> Optional[Union[T, User, Repository]]:
|
|
||||||
if type == 'user':
|
if type == 'user':
|
||||||
if obj := self._user_cache.get(kwargs.get('user')):
|
if obj := self._user_cache.get(kwargs.get('user')):
|
||||||
return obj
|
return obj
|
||||||
|
@ -176,9 +168,7 @@ class GHClient:
|
||||||
|
|
||||||
async def get_issue(self, *, owner: str, repo: str, issue: int) -> Issue:
|
async def get_issue(self, *, owner: str, repo: str, issue: int) -> Issue:
|
||||||
"""Fetch a Github Issue from it's name."""
|
"""Fetch a Github Issue from it's name."""
|
||||||
return Issue(
|
return Issue(await self.http.get_repo_issue(owner, repo, issue), self.http)
|
||||||
await self.http.get_repo_issue(owner, repo, issue), self.http
|
|
||||||
)
|
|
||||||
|
|
||||||
async def create_repo(
|
async def create_repo(
|
||||||
self,
|
self,
|
||||||
|
@ -202,14 +192,10 @@ class GHClient:
|
||||||
"""Fetch a Github gist from it's id."""
|
"""Fetch a Github gist from it's id."""
|
||||||
return Gist(await self.http.get_gist(gist), self.http)
|
return Gist(await self.http.get_gist(gist), self.http)
|
||||||
|
|
||||||
async def create_gist(
|
async def create_gist(self, *, files: List[File], description: str, public: bool) -> Gist:
|
||||||
self, *, files: List[File], description: str, public: bool
|
|
||||||
) -> Gist:
|
|
||||||
"""Creates a Gist with the given files, requires authorisation."""
|
"""Creates a Gist with the given files, requires authorisation."""
|
||||||
return Gist(
|
return Gist(
|
||||||
await self.http.create_gist(
|
await self.http.create_gist(files=files, description=description, public=public),
|
||||||
files=files, description=description, public=public
|
|
||||||
),
|
|
||||||
self.http,
|
self.http,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -23,21 +23,20 @@ __all__ = (
|
||||||
'Organization',
|
'Organization',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def dt_formatter(time_str: str) -> Optional[datetime]:
|
def dt_formatter(time_str: str) -> Optional[datetime]:
|
||||||
if time_str is not None:
|
if time_str is not None:
|
||||||
return datetime.strptime(time_str, r"%Y-%m-%dT%H:%M:%SZ")
|
return datetime.strptime(time_str, r"%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def repr_dt(_datetime: datetime) -> str:
|
def repr_dt(_datetime: datetime) -> str:
|
||||||
return _datetime.strftime(r'%d-%m-%Y, %H:%M:%S')
|
return _datetime.strftime(r'%d-%m-%Y, %H:%M:%S')
|
||||||
|
|
||||||
|
|
||||||
class APIObject:
|
class APIObject:
|
||||||
__slots__: Tuple[str, ...] = (
|
__slots__: Tuple[str, ...] = ('_response', '_http')
|
||||||
'_response',
|
|
||||||
'_http'
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, response: Dict[str, Any], _http: http) -> None:
|
def __init__(self, response: Dict[str, Any], _http: http) -> None:
|
||||||
self._http = _http
|
self._http = _http
|
||||||
|
@ -49,11 +48,13 @@ class APIObject:
|
||||||
|
|
||||||
# === User stuff ===#
|
# === User stuff ===#
|
||||||
|
|
||||||
|
|
||||||
class _BaseUser(APIObject):
|
class _BaseUser(APIObject):
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'login',
|
'login',
|
||||||
'id',
|
'id',
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, response: Dict[str, Any], _http: http) -> None:
|
def __init__(self, response: Dict[str, Any], _http: http) -> None:
|
||||||
super().__init__(response, _http)
|
super().__init__(response, _http)
|
||||||
self._http = _http
|
self._http = _http
|
||||||
|
@ -88,6 +89,7 @@ class User(_BaseUser):
|
||||||
'following',
|
'following',
|
||||||
'created_at',
|
'created_at',
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, response: Dict[str, Any], _http: http) -> None:
|
def __init__(self, response: Dict[str, Any], _http: http) -> None:
|
||||||
super().__init__(response, _http)
|
super().__init__(response, _http)
|
||||||
tmp = self.__slots__ + _BaseUser.__slots__
|
tmp = self.__slots__ + _BaseUser.__slots__
|
||||||
|
@ -128,6 +130,7 @@ class PartialUser(_BaseUser):
|
||||||
|
|
||||||
# === Repository stuff ===#
|
# === Repository stuff ===#
|
||||||
|
|
||||||
|
|
||||||
class Repository(APIObject):
|
class Repository(APIObject):
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
id: int
|
id: int
|
||||||
|
@ -138,8 +141,7 @@ class Repository(APIObject):
|
||||||
'id',
|
'id',
|
||||||
'name',
|
'name',
|
||||||
'owner',
|
'owner',
|
||||||
'size'
|
'size' 'created_at',
|
||||||
'created_at',
|
|
||||||
'url',
|
'url',
|
||||||
'html_url',
|
'html_url',
|
||||||
'archived',
|
'archived',
|
||||||
|
@ -152,6 +154,7 @@ class Repository(APIObject):
|
||||||
'watchers_count',
|
'watchers_count',
|
||||||
'license',
|
'license',
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, response: Dict[str, Any], _http: http) -> None:
|
def __init__(self, response: Dict[str, Any], _http: http) -> None:
|
||||||
super().__init__(response, _http)
|
super().__init__(response, _http)
|
||||||
tmp = self.__slots__ + APIObject.__slots__
|
tmp = self.__slots__ + APIObject.__slots__
|
||||||
|
@ -198,6 +201,7 @@ class Repository(APIObject):
|
||||||
def forks(self) -> int:
|
def forks(self) -> int:
|
||||||
return self._response.get('forks')
|
return self._response.get('forks')
|
||||||
|
|
||||||
|
|
||||||
class Issue(APIObject):
|
class Issue(APIObject):
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'id',
|
'id',
|
||||||
|
@ -241,8 +245,10 @@ class Issue(APIObject):
|
||||||
def html_url(self) -> str:
|
def html_url(self) -> str:
|
||||||
return self._response.get('html_url')
|
return self._response.get('html_url')
|
||||||
|
|
||||||
|
|
||||||
# === Gist stuff ===#
|
# === Gist stuff ===#
|
||||||
|
|
||||||
|
|
||||||
class File:
|
class File:
|
||||||
def __init__(self, fp: Union[str, io.StringIO], filename: str = 'DefaultFilename.txt') -> None:
|
def __init__(self, fp: Union[str, io.StringIO], filename: str = 'DefaultFilename.txt') -> None:
|
||||||
self.fp = fp
|
self.fp = fp
|
||||||
|
@ -264,6 +270,7 @@ class File:
|
||||||
|
|
||||||
raise TypeError(f'Expected str, io.StringIO, or io.BytesIO, got {type(self.fp)}')
|
raise TypeError(f'Expected str, io.StringIO, or io.BytesIO, got {type(self.fp)}')
|
||||||
|
|
||||||
|
|
||||||
class Gist(APIObject):
|
class Gist(APIObject):
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'id',
|
'id',
|
||||||
|
@ -275,6 +282,7 @@ class Gist(APIObject):
|
||||||
'created_at',
|
'created_at',
|
||||||
'truncated',
|
'truncated',
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, response: Dict[str, Any], _http: http) -> None:
|
def __init__(self, response: Dict[str, Any], _http: http) -> None:
|
||||||
super().__init__(response, _http)
|
super().__init__(response, _http)
|
||||||
tmp = self.__slots__ + APIObject.__slots__
|
tmp = self.__slots__ + APIObject.__slots__
|
||||||
|
@ -311,6 +319,7 @@ class Gist(APIObject):
|
||||||
|
|
||||||
# === Organization stuff ===#
|
# === Organization stuff ===#
|
||||||
|
|
||||||
|
|
||||||
class Organization(APIObject):
|
class Organization(APIObject):
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'login',
|
'login',
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue