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:
parent
72bd647c9b
commit
3d49dd9246
5 changed files with 102 additions and 37 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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 ==#
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue