1
Fork 0
mirror of https://github.com/RGBCube/GitHubWrapper synced 2025-05-17 22:45:08 +00:00

Adding files to repos

This commit is contained in:
VarMonke 2022-05-11 11:33:22 +05:30
parent 72bd647c9b
commit 3d49dd9246
5 changed files with 102 additions and 37 deletions

View file

@ -59,6 +59,7 @@ class GHClient:
token: Optional[:class:`str`]
The authenticated Client's token, if applicable.
"""
has_started: bool = False
def __init__(
@ -109,7 +110,7 @@ class GHClient:
raise Exception('HTTP Session doesn\'t exist') from exc
def __repr__(self) -> str:
return f'<{self.__class__.__name__} has_auth={bool(self.__auth)}>'
return f'<Client has_auth={bool(self.__auth)}>'
@overload
def check_limits(self, as_dict: Literal[True] = True) -> Dict[str, Union[str, int]]:
@ -318,7 +319,9 @@ class GHClient:
"""
return Gist(await self.http.get_gist(gist), self.http)
async def create_gist(self, *, files: List[File], description: str, public: bool) -> Gist:
async def create_gist(
self, *, files: List[File], description: str = 'Gist from Github-Api-Wrapper', public: bool = True
) -> Gist:
"""Creates a Gist with the given files, requires authorisation.
Parameters

View file

@ -1,7 +1,7 @@
# == exceptions.py ==#
import datetime
from typing import Tuple
from typing import Optional, Tuple
from aiohttp import ClientResponse
@ -162,3 +162,10 @@ class RepositoryAlreadyExists(ResourceAlreadyExists):
def __init__(self):
msg = 'The requested repository already exists.'
super().__init__(msg)
class FileAlreadyExists(ResourceAlreadyExists):
def __init__(self, msg: Optional[str] = None):
if msg is None:
msg = 'The requested file already exists.'
super().__init__(msg)

View file

@ -1,20 +1,24 @@
# == http.py ==#
from __future__ import annotations
from asyncio.base_subprocess import ReadSubprocessPipeProto
from base64 import b64encode
import json
import re
from datetime import datetime
from types import SimpleNamespace
from typing import Any, Dict, NamedTuple, Optional, Type, Tuple, Union, List
from typing_extensions import TypeAlias
from typing import Any, Dict, Literal, NamedTuple, Optional, Type, Tuple, Union, List
from typing_extensions import TypeAlias, reveal_type
import platform
import aiohttp
from .exceptions import *
from .exceptions import GistNotFound, RepositoryAlreadyExists, MissingPermissions
from .objects import User, Gist, Repository, File
from .exceptions import FileAlreadyExists
from .exceptions import ResourceAlreadyExists
from .objects import User, Gist, Repository, File, bytes_to_b64
from .urls import *
from . import __version__
@ -300,3 +304,23 @@ class http:
if result.status == 401:
raise NoAuthProvided
raise RepositoryAlreadyExists
async def add_file(self, owner: str, repo_name: str, filename: str, content: str, message: str, branch: str):
"""Adds a file to the given repo."""
data = {
'content': bytes_to_b64(content=content),
'message': message,
'branch': branch,
}
result = await self.session.put(ADD_FILE_URL.format(owner, repo_name, filename), data=json.dumps(data))
if 200 <= result.status <= 299:
return await result.json()
if result.status == 401:
raise NoAuthProvided
if result.status == 409:
raise FileAlreadyExists
if result.status == 422:
raise FileAlreadyExists('This file exists, and can only be edited.')
return await result.json(), result.status

View file

@ -1,7 +1,11 @@
# == objects.py ==#
from __future__ import annotations
from base64 import b64encode
import json
from typing import TYPE_CHECKING, Any, Optional, Tuple, Union, Dict, List
from typing import TYPE_CHECKING, Any, Literal, Optional, Tuple, Union, Dict, List
import aiohttp
if TYPE_CHECKING:
from .http import http
@ -35,8 +39,13 @@ def repr_dt(_datetime: datetime) -> str:
return _datetime.strftime(r'%d-%m-%Y, %H:%M:%S')
def bytes_to_b64(content) -> str:
return b64encode(content.encode('utf-8')).decode('ascii')
class APIObject:
"""Top level class for objects created from the API"""
__slots__: Tuple[str, ...] = ('_response', '_http')
def __init__(self, response: Dict[str, Any], _http: http) -> None:
@ -102,6 +111,7 @@ class User(_BaseUser):
created_at: :class:`datetime.datetime`
The time of creation of the user.
"""
__slots__ = (
'login',
'id',
@ -130,7 +140,6 @@ class User(_BaseUser):
return f'<{self.__class__.__name__} login: {self.login!r}, id: {self.id}, created_at: {self.created_at}>'
class PartialUser(_BaseUser):
__slots__ = (
'site_admin',
@ -182,6 +191,7 @@ class Repository(APIObject):
default_branch: :class:`str`
The name of the default branch of the repository.
"""
if TYPE_CHECKING:
id: int
name: str
@ -198,7 +208,6 @@ class Repository(APIObject):
'disabled',
'updated_at',
'open_issues_count',
'default_branch',
'clone_url',
'stargazers_count',
'watchers_count',
@ -253,9 +262,25 @@ class Repository(APIObject):
def forks(self) -> int:
return self._response.get('forks')
@property
def default_branch(self) -> str:
""":class:`str`: The default branch of the repository."""
return self._response.get('default_branch')
async def delete(self) -> None:
"""Deletes the repository."""
return await self._http.delete_repo(self.owner.name, self.name,) #type: ignore
return await self._http.delete_repo(
self.owner.name, # type: ignore this shit is not my fault
self.name,
) # type: ignore
async def add_file(self, filename: str, message: str, content: str, branch: Optional[str] = None) -> None:
"""Adds a file to the repository."""
if branch is None:
branch = self.default_branch
return await self._http.add_file(owner=self.owner.name, repo_name=self.name, filename=filename, content=content, message=message, branch=branch) # type: ignore
class Issue(APIObject):
@ -278,6 +303,7 @@ class Issue(APIObject):
closed_by: Optional[Union[:class:`PartialUser`, :class:`User`]]
The user the issue was closed by, if applicable.
"""
__slots__ = (
'id',
'title',
@ -331,13 +357,14 @@ class File:
Parameters
----------
fp: Union[:class:`str`, :class:`io.StringIO`]
fp: Union[:class:`str`, :class:`io.StringIO`, :class:`io.BytesIO`]
The filepath or StringIO representing a file to upload.
If providing a StringIO instance, a filename shuold also be provided to the file.
filename: :class:`str`
An override to the file's name, encouraged to provide this if using a StringIO instance.
"""
def __init__(self, fp: Union[str, io.StringIO], filename: str = 'DefaultFilename.txt') -> None:
def __init__(self, fp: Union[str, io.StringIO, io.BytesIO], filename: str = 'DefaultFilename.txt') -> None:
self.fp = fp
self.filename = filename
@ -346,12 +373,10 @@ class File:
if os.path.exists(self.fp):
with open(self.fp) as fp:
data = fp.read()
return data
return self.fp
elif isinstance(self.fp, io.BytesIO):
return self.fp.read()
return self.fp.read().decode('utf-8')
elif isinstance(self.fp, io.StringIO): # type: ignore
return self.fp.getvalue()
@ -376,6 +401,7 @@ class Gist(APIObject):
created_at: :class:`datetime.datetime`
The time the gist was created at.
"""
__slots__ = (
'id',
'html_url',
@ -444,6 +470,7 @@ class Organization(APIObject):
avatar_url: :class:`str`
The url of the organization's avatar.
"""
__slots__ = (
'login',
'id',

View file

@ -28,6 +28,10 @@ REPOS_URL = BASE_URL + '/repos/{0}' # repos of a user
REPO_URL = BASE_URL + '/repos/{0}/{1}' # a specific repo
ADD_FILE_URL = BASE_URL + '/repos/{}/{}/contents/{}'
ADD_FILE_BRANCH = BASE_URL + ''
REPO_ISSUE_URL = REPO_URL + '/issues/{2}' # a specific issue
# == gist urls ==#