(style) reformatted with python black for easier merges

pull/27/head
Langenfeld 2023-08-21 10:13:30 +02:00
rodzic 3b8f04e31b
commit 9f6785cdee
11 zmienionych plików z 343 dodań i 223 usunięć

Wyświetl plik

@ -41,10 +41,11 @@ other_user = User.request(gitea, "OtherUserName")
print(other_user.username) print(other_user.username)
``` ```
Note that the fields of the User, Organization,... classes are dynamically created at runtime, and thus not visible during divelopment. Refer to the Gitea-API documentation for the fields names. Note that the fields of the User, Organization,... classes are dynamically created at runtime, and thus not visible
during divelopment. Refer to the Gitea-API documentation for the fields names.
Fields that can not be altered via gitea-api, are read only. After altering a field, the `.commit` method of the
Fields that can not be altered via gitea-api, are read only. After altering a field, the `.commit` method of the according object must be called to synchronize the changed fields with your gitea instance. according object must be called to synchronize the changed fields with your gitea instance.
```python ```python
org = Organization.request(gitea, test_org) org = Organization.request(gitea, test_org)
@ -54,11 +55,13 @@ org.commit()
``` ```
An entity in gitea can be deleted by calling delete. An entity in gitea can be deleted by calling delete.
```python ```python
org.delete() org.delete()
``` ```
All entity objects do have methods to execute some of the requests possible though the gitea-api: All entity objects do have methods to execute some of the requests possible though the gitea-api:
```python ```python
org = Organization.request(gitea, ORGNAME) org = Organization.request(gitea, ORGNAME)
teams = org.get_teams() teams = org.get_teams()
@ -68,7 +71,6 @@ for team in teams:
print(repo.name) print(repo.name)
``` ```
## Installation ## Installation
Use ``pip install py-gitea`` to install. Use ``pip install py-gitea`` to install.

Wyświetl plik

@ -15,21 +15,21 @@ from .apiobject import (
Milestone, Milestone,
Commit, Commit,
Comment, Comment,
Content Content,
) )
__all__ = [ __all__ = [
'Gitea', "Gitea",
'User', "User",
'Organization', "Organization",
'Team', "Team",
'Repository', "Repository",
'Branch', "Branch",
'NotFoundException', "NotFoundException",
'AlreadyExistsException', "AlreadyExistsException",
'Issue', "Issue",
'Milestone', "Milestone",
'Commit', "Commit",
'Comment', "Comment",
'Content' "Content",
] ]

Wyświetl plik

@ -21,18 +21,19 @@ class Organization(ApiObject):
super().__init__(gitea) super().__init__(gitea)
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Organization): return False if not isinstance(other, Organization):
return False
return self.gitea == other.gitea and self.name == other.name return self.gitea == other.gitea and self.name == other.name
def __hash__(self): def __hash__(self):
return hash(self.gitea) ^ hash(self.name) return hash(self.gitea) ^ hash(self.name)
@classmethod @classmethod
def request(cls, gitea: 'Gitea', name: str) -> 'Organization': def request(cls, gitea: "Gitea", name: str) -> "Organization":
return cls._request(gitea, {"name": name}) return cls._request(gitea, {"name": name})
@classmethod @classmethod
def parse_response(cls, gitea, result) -> 'Organization': def parse_response(cls, gitea, result) -> "Organization":
api_object = super().parse_response(gitea, result) api_object = super().parse_response(gitea, result)
# add "name" field to make this behave similar to users for gitea < 1.18 # add "name" field to make this behave similar to users for gitea < 1.18
# also necessary for repository-owner when org is repo owner # also necessary for repository-owner when org is repo owner
@ -40,14 +41,18 @@ class Organization(ApiObject):
Organization._add_read_property("name", result["username"], api_object) Organization._add_read_property("name", result["username"], api_object)
return api_object return api_object
_patchable_fields = {"description", "full_name", "location", "visibility", "website"} _patchable_fields = {
"description",
"full_name",
"location",
"visibility",
"website",
}
def commit(self): def commit(self):
values = self.get_dirty_fields() values = self.get_dirty_fields()
args = {"name": self.name} args = {"name": self.name}
self.gitea.requests_patch( self.gitea.requests_patch(Organization.API_OBJECT.format(**args), data=values)
Organization.API_OBJECT.format(**args), data=values
)
self.dirty_fields = {} self.dirty_fields = {}
def create_repo( def create_repo(
@ -83,7 +88,9 @@ class Organization(ApiObject):
}, },
) )
if "id" in result: if "id" in result:
self.gitea.logger.info("Successfully created Repository %s " % result["name"]) self.gitea.logger.info(
"Successfully created Repository %s " % result["name"]
)
else: else:
self.gitea.logger.error(result["message"]) self.gitea.logger.error(result["message"])
raise Exception("Repository not created... (gitea: %s)" % result["message"]) raise Exception("Repository not created... (gitea: %s)" % result["message"])
@ -108,7 +115,8 @@ class Organization(ApiObject):
) )
teams = [Team.parse_response(self.gitea, result) for result in results] teams = [Team.parse_response(self.gitea, result) for result in results]
# organisation seems to be missing using this request, so we add org manually # organisation seems to be missing using this request, so we add org manually
for t in teams: setattr(t, "_organization", self) for t in teams:
setattr(t, "_organization", self)
return teams return teams
def get_team(self, name) -> "Team": def get_team(self, name) -> "Team":
@ -167,7 +175,8 @@ class User(ApiObject):
self._emails = [] self._emails = []
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, User): return False if not isinstance(other, User):
return False
return self.gitea == other.gitea and self.id == other.id return self.gitea == other.gitea and self.id == other.id
def __hash__(self): def __hash__(self):
@ -179,7 +188,7 @@ class User(ApiObject):
return self._emails return self._emails
@classmethod @classmethod
def request(cls, gitea: 'Gitea', name: str) -> "User": def request(cls, gitea: "Gitea", name: str) -> "User":
api_object = cls._request(gitea, {"name": name}) api_object = cls._request(gitea, {"name": name})
return api_object return api_object
@ -249,7 +258,9 @@ class User(ApiObject):
}, },
) )
if "id" in result: if "id" in result:
self.gitea.logger.info("Successfully created Repository %s " % result["name"]) self.gitea.logger.info(
"Successfully created Repository %s " % result["name"]
)
else: else:
self.gitea.logger.error(result["message"]) self.gitea.logger.error(result["message"])
raise Exception("Repository not created... (gitea: %s)" % result["message"]) raise Exception("Repository not created... (gitea: %s)" % result["message"])
@ -267,12 +278,12 @@ class User(ApiObject):
results = self.gitea.requests_get_paginated(url) results = self.gitea.requests_get_paginated(url)
return [Organization.parse_response(self.gitea, result) for result in results] return [Organization.parse_response(self.gitea, result) for result in results]
def get_teams(self) -> List['Team']: def get_teams(self) -> List["Team"]:
url = f"/user/teams" url = f"/user/teams"
results = self.gitea.requests_get_paginated(url, sudo=self) results = self.gitea.requests_get_paginated(url, sudo=self)
return [Team.parse_response(self.gitea, result) for result in results] return [Team.parse_response(self.gitea, result) for result in results]
def get_accessible_repos(self) -> List['Repository']: def get_accessible_repos(self) -> List["Repository"]:
"""Get all Repositories accessible by the logged in User.""" """Get all Repositories accessible by the logged in User."""
results = self.gitea.requests_get("/user/repos", sudo=self) results = self.gitea.requests_get("/user/repos", sudo=self)
return [Repository.parse_response(self, result) for result in results] return [Repository.parse_response(self, result) for result in results]
@ -300,7 +311,6 @@ class User(ApiObject):
class Branch(ReadonlyApiObject): class Branch(ReadonlyApiObject):
def __init__(self, gitea): def __init__(self, gitea):
super().__init__(gitea) super().__init__(gitea)
@ -318,13 +328,15 @@ class Branch(ReadonlyApiObject):
} }
@classmethod @classmethod
def request(cls, gitea: 'Gitea', owner: str, repo: str, ref: str): def request(cls, gitea: "Gitea", owner: str, repo: str, ref: str):
return cls._request(gitea, {"owner": owner, "repo": repo, "ref": ref}) return cls._request(gitea, {"owner": owner, "repo": repo, "ref": ref})
class Repository(ApiObject): class Repository(ApiObject):
API_OBJECT = """/repos/{owner}/{name}""" # <owner>, <reponame> API_OBJECT = """/repos/{owner}/{name}""" # <owner>, <reponame>
REPO_IS_COLLABORATOR = """/repos/%s/%s/collaborators/%s""" # <owner>, <reponame>, <username> REPO_IS_COLLABORATOR = (
"""/repos/%s/%s/collaborators/%s""" # <owner>, <reponame>, <username>
)
REPO_SEARCH = """/repos/search/%s""" # <reponame> REPO_SEARCH = """/repos/search/%s""" # <reponame>
REPO_BRANCHES = """/repos/%s/%s/branches""" # <owner>, <reponame> REPO_BRANCHES = """/repos/%s/%s/branches""" # <owner>, <reponame>
REPO_ISSUES = """/repos/{owner}/{repo}/issues""" # <owner, reponame> REPO_ISSUES = """/repos/{owner}/{repo}/issues""" # <owner, reponame>
@ -339,7 +351,8 @@ class Repository(ApiObject):
super().__init__(gitea) super().__init__(gitea)
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Repository): return False if not isinstance(other, Repository):
return False
return self.owner == other.owner and self.name == other.name return self.owner == other.owner and self.name == other.name
def __hash__(self): def __hash__(self):
@ -348,12 +361,13 @@ class Repository(ApiObject):
_fields_to_parsers = { _fields_to_parsers = {
# dont know how to tell apart user and org as owner except form email being empty. # dont know how to tell apart user and org as owner except form email being empty.
"owner": lambda gitea, r: Organization.parse_response(gitea, r) "owner": lambda gitea, r: Organization.parse_response(gitea, r)
if r["email"] == "" else User.parse_response(gitea, r), if r["email"] == ""
else User.parse_response(gitea, r),
"updated_at": lambda gitea, t: Util.convert_time(t), "updated_at": lambda gitea, t: Util.convert_time(t),
} }
@classmethod @classmethod
def request(cls, gitea: 'Gitea', owner: str, name: str): def request(cls, gitea: "Gitea", owner: str, name: str):
return cls._request(gitea, {"owner": owner, "name": name}) return cls._request(gitea, {"owner": owner, "name": name})
_patchable_fields = { _patchable_fields = {
@ -391,7 +405,7 @@ class Repository(ApiObject):
self.gitea.requests_patch(self.API_OBJECT.format(**args), data=values) self.gitea.requests_patch(self.API_OBJECT.format(**args), data=values)
self.dirty_fields = {} self.dirty_fields = {}
def get_branches(self) -> List['Branch']: def get_branches(self) -> List["Branch"]:
"""Get all the Branches of this Repository.""" """Get all the Branches of this Repository."""
results = self.gitea.requests_get( results = self.gitea.requests_get(
Repository.REPO_BRANCHES % (self.owner.username, self.name) Repository.REPO_BRANCHES % (self.owner.username, self.name)
@ -431,7 +445,8 @@ class Repository(ApiObject):
issues = [] issues = []
data = {"state": state} data = {"state": state}
results = self.gitea.requests_get_paginated( results = self.gitea.requests_get_paginated(
Repository.REPO_ISSUES.format(owner=self.owner.username, repo=self.name), params=data Repository.REPO_ISSUES.format(owner=self.owner.username, repo=self.name),
params=data,
) )
for result in results: for result in results:
issue = Issue.parse_response(self.gitea, result) issue = Issue.parse_response(self.gitea, result)
@ -467,14 +482,20 @@ class Repository(ApiObject):
"title": title, "title": title,
} }
result = self.gitea.requests_post( result = self.gitea.requests_post(
Repository.REPO_ISSUES.format(owner=self.owner.username, repo=self.name), data=data Repository.REPO_ISSUES.format(owner=self.owner.username, repo=self.name),
data=data,
) )
return Issue.parse_response(self.gitea, result) return Issue.parse_response(self.gitea, result)
def create_milestone(self, title: str, description: str, due_date: str = None, state: str = "open") -> "Milestone": def create_milestone(
url = Repository.REPO_MILESTONES.format(owner=self.owner.username, repo=self.name) self, title: str, description: str, due_date: str = None, state: str = "open"
) -> "Milestone":
url = Repository.REPO_MILESTONES.format(
owner=self.owner.username, repo=self.name
)
data = {"title": title, "description": description, "state": state} data = {"title": title, "description": description, "state": state}
if due_date: data["due_date"] = due_date if due_date:
data["due_date"] = due_date
result = self.gitea.requests_post(url, data=data) result = self.gitea.requests_post(url, data=data)
return Milestone.parse_response(self.gitea, result) return Milestone.parse_response(self.gitea, result)
@ -528,11 +549,17 @@ class Repository(ApiObject):
url = f"/repos/{self.owner.username}/{self.name}/collaborators/{user_name}" url = f"/repos/{self.owner.username}/{self.name}/collaborators/{user_name}"
self.gitea.requests_delete(url) self.gitea.requests_delete(url)
def transfer_ownership(self, new_owner: Union["User", "Organization"], new_teams: Set["Team"] = frozenset()): def transfer_ownership(
self,
new_owner: Union["User", "Organization"],
new_teams: Set["Team"] = frozenset(),
):
url = Repository.REPO_TRANSFER.format(owner=self.owner.username, repo=self.name) url = Repository.REPO_TRANSFER.format(owner=self.owner.username, repo=self.name)
data = {"new_owner": new_owner.username} data = {"new_owner": new_owner.username}
if isinstance(new_owner, Organization): if isinstance(new_owner, Organization):
new_team_ids = [team.id for team in new_teams if team in new_owner.get_teams()] new_team_ids = [
team.id for team in new_teams if team in new_owner.get_teams()
]
data["team_ids"] = new_team_ids data["team_ids"] = new_team_ids
self.gitea.requests_post(url, data=data) self.gitea.requests_post(url, data=data)
# TODO: make sure this instance is either updated or discarded # TODO: make sure this instance is either updated or discarded
@ -541,17 +568,25 @@ class Repository(ApiObject):
"""https://try.gitea.io/api/swagger#/repository/repoGetContentsList""" """https://try.gitea.io/api/swagger#/repository/repoGetContentsList"""
url = f"/repos/{self.owner.username}/{self.name}/contents" url = f"/repos/{self.owner.username}/{self.name}/contents"
data = {"ref": commit.sha} if commit else {} data = {"ref": commit.sha} if commit else {}
result = [Content.parse_response(self.gitea, f) for f in self.gitea.requests_get(url, data)] result = [
Content.parse_response(self.gitea, f)
for f in self.gitea.requests_get(url, data)
]
return result return result
def get_file_content(self, content: "Content", commit: "Commit" = None) -> Union[str, List["Content"]]: def get_file_content(
self, content: "Content", commit: "Commit" = None
) -> Union[str, List["Content"]]:
"""https://try.gitea.io/api/swagger#/repository/repoGetContents""" """https://try.gitea.io/api/swagger#/repository/repoGetContents"""
url = f"/repos/{self.owner.username}/{self.name}/contents/{content.path}" url = f"/repos/{self.owner.username}/{self.name}/contents/{content.path}"
data = {"ref": commit.sha} if commit else {} data = {"ref": commit.sha} if commit else {}
if content.type == Content.FILE: if content.type == Content.FILE:
return self.gitea.requests_get(url, data)["content"] return self.gitea.requests_get(url, data)["content"]
else: else:
return [Content.parse_response(self.gitea, f) for f in self.gitea.requests_get(url, data)] return [
Content.parse_response(self.gitea, f)
for f in self.gitea.requests_get(url, data)
]
def create_file(self, file_path: str, content: str, data: dict = None): def create_file(self, file_path: str, content: str, data: dict = None):
"""https://try.gitea.io/api/swagger#/repository/repoCreateFile""" """https://try.gitea.io/api/swagger#/repository/repoCreateFile"""
@ -561,7 +596,9 @@ class Repository(ApiObject):
data.update({"content": content}) data.update({"content": content})
return self.gitea.requests_post(url, data) return self.gitea.requests_post(url, data)
def change_file(self, file_path: str, file_sha: str, content: str, data: dict = None): def change_file(
self, file_path: str, file_sha: str, content: str, data: dict = None
):
"""https://try.gitea.io/api/swagger#/repository/repoCreateFile""" """https://try.gitea.io/api/swagger#/repository/repoCreateFile"""
if not data: if not data:
data = {} data = {}
@ -583,7 +620,8 @@ class Milestone(ApiObject):
super().__init__(gitea) super().__init__(gitea)
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Milestone): return False if not isinstance(other, Milestone):
return False
return self.gitea == other.gitea and self.id == other.id return self.gitea == other.gitea and self.id == other.id
def __hash__(self): def __hash__(self):
@ -612,17 +650,17 @@ class Milestone(ApiObject):
} }
@classmethod @classmethod
def request(cls, gitea: 'Gitea', owner: str, repo: str, number: str): def request(cls, gitea: "Gitea", owner: str, repo: str, number: str):
return cls._request(gitea, {"owner": owner, "repo": repo, "number": number}) return cls._request(gitea, {"owner": owner, "repo": repo, "number": number})
class Comment(ApiObject): class Comment(ApiObject):
def __init__(self, gitea): def __init__(self, gitea):
super().__init__(gitea) super().__init__(gitea)
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Comment): return False if not isinstance(other, Comment):
return False
return self.repo == other.repo and self.id == other.id return self.repo == other.repo and self.id == other.id
def __hash__(self): def __hash__(self):
@ -636,24 +674,26 @@ class Comment(ApiObject):
class Commit(ReadonlyApiObject): class Commit(ReadonlyApiObject):
def __init__(self, gitea): def __init__(self, gitea):
super().__init__(gitea) super().__init__(gitea)
_fields_to_parsers = { _fields_to_parsers = {
# NOTE: api may return None for commiters that are no gitea users # NOTE: api may return None for commiters that are no gitea users
"author": lambda gitea, u: User.parse_response(gitea, u) if u else None "author": lambda gitea, u: User.parse_response(gitea, u)
if u
else None
} }
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Commit): return False if not isinstance(other, Commit):
return False
return self.sha == other.sha return self.sha == other.sha
def __hash__(self): def __hash__(self):
return hash(self.sha) return hash(self.sha)
@classmethod @classmethod
def parse_response(cls, gitea, result) -> 'Commit': def parse_response(cls, gitea, result) -> "Commit":
commit_cache = result["commit"] commit_cache = result["commit"]
api_object = cls(gitea) api_object = cls(gitea)
cls._initialize(gitea, api_object, result) cls._initialize(gitea, api_object, result)
@ -675,7 +715,8 @@ class Issue(ApiObject):
super().__init__(gitea) super().__init__(gitea)
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Issue): return False if not isinstance(other, Issue):
return False
return self.repo == other.repo and self.id == other.id return self.repo == other.repo and self.id == other.id
def __hash__(self): def __hash__(self):
@ -688,7 +729,7 @@ class Issue(ApiObject):
"assignees": lambda gitea, us: [User.parse_response(gitea, u) for u in us], "assignees": lambda gitea, us: [User.parse_response(gitea, u) for u in us],
"state": lambda gitea, s: Issue.CLOSED if s == "closed" else Issue.OPENED, "state": lambda gitea, s: Issue.CLOSED if s == "closed" else Issue.OPENED,
# Repository in this request is just a "RepositoryMeta" record, thus request whole object # Repository in this request is just a "RepositoryMeta" record, thus request whole object
"repository": lambda gitea, r: Repository.request(gitea, r["owner"], r["name"]) "repository": lambda gitea, r: Repository.request(gitea, r["owner"], r["name"]),
} }
_parsers_to_fields = { _parsers_to_fields = {
@ -707,13 +748,19 @@ class Issue(ApiObject):
def commit(self): def commit(self):
values = self.get_dirty_fields() values = self.get_dirty_fields()
args = {"owner": self.repository.owner.username, "repo": self.repository.name, "index": self.number} args = {
"owner": self.repository.owner.username,
"repo": self.repository.name,
"index": self.number,
}
self.gitea.requests_patch(Issue.API_OBJECT.format(**args), data=values) self.gitea.requests_patch(Issue.API_OBJECT.format(**args), data=values)
self.dirty_fields = {} self.dirty_fields = {}
@classmethod @classmethod
def request(cls, gitea: 'Gitea', owner: str, repo: str, number: str): def request(cls, gitea: "Gitea", owner: str, repo: str, number: str):
api_object = cls._request(gitea, {"owner": owner, "repo": repo, "index": number}) api_object = cls._request(
gitea, {"owner": owner, "repo": repo, "index": number}
)
return api_object return api_object
@classmethod @classmethod
@ -774,7 +821,8 @@ class Team(ApiObject):
super().__init__(gitea) super().__init__(gitea)
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Team): return False if not isinstance(other, Team):
return False
return self.organization == other.organization and self.id == other.id return self.organization == other.organization and self.id == other.id
def __hash__(self): def __hash__(self):
@ -838,8 +886,11 @@ class Content(ReadonlyApiObject):
super().__init__(gitea) super().__init__(gitea)
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Team): return False if not isinstance(other, Team):
return self.repo == self.repo and self.sha == other.sha and self.name == other.name return False
return (
self.repo == self.repo and self.sha == other.sha and self.name == other.name
)
def __hash__(self): def __hash__(self):
return hash(self.repo) ^ hash(self.sha) ^ hash(self.name) return hash(self.repo) ^ hash(self.sha) ^ hash(self.name)

Wyświetl plik

@ -1,8 +1,11 @@
from .exceptions import ObjectIsInvalid, MissiongEqualyImplementation, RawRequestEndpointMissing from .exceptions import (
ObjectIsInvalid,
MissiongEqualyImplementation,
RawRequestEndpointMissing,
)
class ReadonlyApiObject: class ReadonlyApiObject:
def __init__(self, gitea): def __init__(self, gitea):
self.gitea = gitea self.gitea = gitea
self.deleted = False # set if .delete was called, so that an exception is risen self.deleted = False # set if .delete was called, so that an exception is risen
@ -61,8 +64,7 @@ class ReadonlyApiObject:
def _add_read_property(cls, name, value, api_object): def _add_read_property(cls, name, value, api_object):
if not hasattr(api_object, name): if not hasattr(api_object, name):
setattr(api_object, "_" + name, value) setattr(api_object, "_" + name, value)
prop = property( prop = property((lambda n: lambda self: self._get_var(n))(name))
(lambda n: lambda self: self._get_var(n))(name))
setattr(cls, name, prop) setattr(cls, name, prop)
else: else:
raise AttributeError(f"Attribute {name} already exists on api object.") raise AttributeError(f"Attribute {name} already exists on api object.")
@ -107,7 +109,8 @@ class ApiObject(ReadonlyApiObject):
setattr(api_object, "_" + name, value) setattr(api_object, "_" + name, value)
prop = property( prop = property(
(lambda n: lambda self: self._get_var(n))(name), (lambda n: lambda self: self._get_var(n))(name),
(lambda n: lambda self, v: self.__set_var(n, v))(name)) (lambda n: lambda self, v: self.__set_var(n, v))(name),
)
setattr(cls, name, prop) setattr(cls, name, prop)
def __set_var(self, name, i): def __set_var(self, name, i):

Wyświetl plik

@ -17,6 +17,7 @@ class ConflictException(Exception):
class RawRequestEndpointMissing(Exception): class RawRequestEndpointMissing(Exception):
"""This ApiObject can only be obtained through other api objects and does not have """This ApiObject can only be obtained through other api objects and does not have
diret .request method.""" diret .request method."""
pass pass
@ -25,4 +26,5 @@ class MissiongEqualyImplementation(Exception):
Each Object obtained from the gitea api must be able to check itself for equality in relation to its Each Object obtained from the gitea api must be able to check itself for equality in relation to its
fields obtained from gitea. Risen if an api object is lacking the proper implementation. fields obtained from gitea. Risen if an api object is lacking the proper implementation.
""" """
pass pass

Wyświetl plik

@ -12,6 +12,7 @@ from .exceptions import NotFoundException, ConflictException, AlreadyExistsExcep
class Gitea: class Gitea:
"""Object to establish a session with Gitea.""" """Object to establish a session with Gitea."""
ADMIN_CREATE_USER = """/admin/users""" ADMIN_CREATE_USER = """/admin/users"""
GET_USERS_ADMIN = """/admin/users""" GET_USERS_ADMIN = """/admin/users"""
ADMIN_REPO_CREATE = """/admin/users/%s/repos""" # <ownername> ADMIN_REPO_CREATE = """/admin/users/%s/repos""" # <ownername>
@ -21,12 +22,7 @@ class Gitea:
CREATE_TEAM = """/orgs/%s/teams""" # <orgname> CREATE_TEAM = """/orgs/%s/teams""" # <orgname>
def __init__( def __init__(
self, self, gitea_url: str, token_text=None, auth=None, verify=True, log_level="INFO"
gitea_url: str,
token_text=None,
auth=None,
verify=True,
log_level="INFO"
): ):
"""Initializing Gitea-instance """Initializing Gitea-instance
@ -60,7 +56,6 @@ class Gitea:
if not verify: if not verify:
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def __get_url(self, endpoint): def __get_url(self, endpoint):
url = self.url + "/api/v1" + endpoint url = self.url + "/api/v1" + endpoint
self.logger.debug("Url: %s" % url) self.logger.debug("Url: %s" % url)
@ -78,19 +73,25 @@ class Gitea:
combined_params.update(params) combined_params.update(params)
if sudo: if sudo:
combined_params["sudo"] = sudo.username combined_params["sudo"] = sudo.username
request = self.requests.get(self.__get_url(endpoint), headers=self.headers, params=combined_params) request = self.requests.get(
self.__get_url(endpoint), headers=self.headers, params=combined_params
)
if request.status_code not in [200, 201]: if request.status_code not in [200, 201]:
message = f"Received status code: {request.status_code} ({request.url})" message = f"Received status code: {request.status_code} ({request.url})"
if request.status_code in [404]: if request.status_code in [404]:
raise NotFoundException(message) raise NotFoundException(message)
if request.status_code in [403]: if request.status_code in [403]:
raise Exception(f"Unauthorized: {request.url} - Check your permissions and try again! ({message})") raise Exception(
f"Unauthorized: {request.url} - Check your permissions and try again! ({message})"
)
if request.status_code in [409]: if request.status_code in [409]:
raise ConflictException(message) raise ConflictException(message)
raise Exception(message) raise Exception(message)
return self.parse_result(request) return self.parse_result(request)
def requests_get_paginated(self, endpoint: str, params=frozendict(), sudo=None, page_key: str = "page"): def requests_get_paginated(
self, endpoint: str, params=frozendict(), sudo=None, page_key: str = "page"
):
page = 1 page = 1
combined_params = {} combined_params = {}
combined_params.update(params) combined_params.update(params)
@ -106,7 +107,9 @@ class Gitea:
def requests_put(self, endpoint: str, data: dict = None): def requests_put(self, endpoint: str, data: dict = None):
if not data: if not data:
data = {} data = {}
request = self.requests.put(self.__get_url(endpoint), headers=self.headers, data=json.dumps(data)) request = self.requests.put(
self.__get_url(endpoint), headers=self.headers, data=json.dumps(data)
)
if request.status_code not in [200, 204]: if request.status_code not in [200, 204]:
message = f"Received status code: {request.status_code} ({request.url}) {request.text}" message = f"Received status code: {request.status_code} ({request.url}) {request.text}"
self.logger.error(message) self.logger.error(message)
@ -120,21 +123,34 @@ class Gitea:
raise Exception(message) raise Exception(message)
def requests_post(self, endpoint: str, data: dict): def requests_post(self, endpoint: str, data: dict):
request = self.requests.post(self.__get_url(endpoint), headers=self.headers, data=json.dumps(data)) request = self.requests.post(
self.__get_url(endpoint), headers=self.headers, data=json.dumps(data)
)
if request.status_code not in [200, 201, 202]: if request.status_code not in [200, 201, 202]:
if ("already exists" in request.text or "e-mail already in use" in request.text): if (
"already exists" in request.text
or "e-mail already in use" in request.text
):
self.logger.warning(request.text) self.logger.warning(request.text)
raise AlreadyExistsException() raise AlreadyExistsException()
self.logger.error(f"Received status code: {request.status_code} ({request.url})") self.logger.error(
f"Received status code: {request.status_code} ({request.url})"
)
self.logger.error(f"With info: {data} ({self.headers})") self.logger.error(f"With info: {data} ({self.headers})")
self.logger.error(f"Answer: {request.text}") self.logger.error(f"Answer: {request.text}")
raise Exception(f"Received status code: {request.status_code} ({request.url}), {request.text}") raise Exception(
f"Received status code: {request.status_code} ({request.url}), {request.text}"
)
return self.parse_result(request) return self.parse_result(request)
def requests_patch(self, endpoint: str, data: dict): def requests_patch(self, endpoint: str, data: dict):
request = self.requests.patch(self.__get_url(endpoint), headers=self.headers, data=json.dumps(data)) request = self.requests.patch(
self.__get_url(endpoint), headers=self.headers, data=json.dumps(data)
)
if request.status_code not in [200, 201]: if request.status_code not in [200, 201]:
error_message = f"Received status code: {request.status_code} ({request.url}) {data}" error_message = (
f"Received status code: {request.status_code} ({request.url}) {data}"
)
self.logger.error(error_message) self.logger.error(error_message)
raise Exception(error_message) raise Exception(error_message)
return self.parse_result(request) return self.parse_result(request)

Wyświetl plik

@ -1,35 +1,31 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
with open('README.md') as readme_file: with open("README.md") as readme_file:
README = readme_file.read() README = readme_file.read()
setup_args = dict( setup_args = dict(
name='py-gitea', name="py-gitea",
version='0.2.6', version="0.2.6",
description='A python wrapper for the Gitea API', description="A python wrapper for the Gitea API",
long_description_content_type="text/markdown", long_description_content_type="text/markdown",
long_description=README, long_description=README,
license='MIT', license="MIT",
packages=find_packages(), packages=find_packages(),
author='Vincent Langenfeld ', author="Vincent Langenfeld ",
author_email='langenfv@tf.uni-freiburg.de', author_email="langenfv@tf.uni-freiburg.de",
keywords=['Gitea','api','wrapper'], keywords=["Gitea", "api", "wrapper"],
url='https://github.com/Langenfeld/py-gitea', url="https://github.com/Langenfeld/py-gitea",
download_url='https://pypi.org/project/py-gitea/' download_url="https://pypi.org/project/py-gitea/",
) )
install_requires = [ install_requires = [
'requests', "requests",
'frozendict', "frozendict",
] ]
extras_require = { extras_require = {"test": ["pytest"]}
'test': ['pytest']
}
if __name__ == '__main__': if __name__ == "__main__":
setup( setup(
**setup_args, **setup_args, install_requires=install_requires, extras_require=extras_require
install_requires=install_requires,
extras_require=extras_require
) )

Wyświetl plik

@ -13,6 +13,7 @@ import pytest
from gitea import Gitea from gitea import Gitea
@pytest.fixture @pytest.fixture
def instance(scope="module"): def instance(scope="module"):
try: try:

Wyświetl plik

@ -6,6 +6,7 @@ import uuid
from gitea import Gitea, User, Organization, Team, Repository, Issue, Milestone from gitea import Gitea, User, Organization, Team, Repository, Issue, Milestone
from gitea import NotFoundException, AlreadyExistsException from gitea import NotFoundException, AlreadyExistsException
# put a ".token" file into your directory containg only the token for gitea # put a ".token" file into your directory containg only the token for gitea
@pytest.fixture @pytest.fixture
def instance(scope="module"): def instance(scope="module"):
@ -22,10 +23,13 @@ def instance(scope="module"):
- Token at .token \ - Token at .token \
?" ?"
# make up some fresh names for the tests run # make up some fresh names for the tests run
test_org = "org_" + uuid.uuid4().hex[:8] test_org = "org_" + uuid.uuid4().hex[:8]
test_user = "user_" + uuid.uuid4().hex[:8] test_user = "user_" + uuid.uuid4().hex[:8]
test_team = "team_" + uuid.uuid4().hex[:8] # team names seem to have a rather low max lenght test_team = (
"team_" + uuid.uuid4().hex[:8]
) # team names seem to have a rather low max lenght
test_repo = "repo_" + uuid.uuid4().hex[:8] test_repo = "repo_" + uuid.uuid4().hex[:8]
@ -65,6 +69,7 @@ def test_create_user(instance):
assert type(user.id) is int assert type(user.id) is int
assert user.is_admin is False assert user.is_admin is False
def test_change_user(instance): def test_change_user(instance):
user = instance.get_user_by_name(test_user) user = instance.get_user_by_name(test_user)
location = "a house" location = "a house"
@ -72,7 +77,7 @@ def test_change_user(instance):
new_fullname = "Other Test Full Name" new_fullname = "Other Test Full Name"
user.full_name = new_fullname user.full_name = new_fullname
user.commit(user.username, 0) user.commit(user.username, 0)
del(user) del user
user = instance.get_user_by_name(test_user) user = instance.get_user_by_name(test_user)
assert user.full_name == new_fullname assert user.full_name == new_fullname
assert user.location == location assert user.location == location
@ -153,6 +158,7 @@ def test_list_branches(instance):
master = [b for b in branches if b.name == "master"] master = [b for b in branches if b.name == "master"]
assert len(master) > 0 assert len(master) > 0
def test_list_files_and_content(instance): def test_list_files_and_content(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = org.get_repository(test_repo) repo = org.get_repository(test_repo)
@ -165,13 +171,13 @@ def test_list_files_and_content(instance):
assert len(readme_content) > 0 assert len(readme_content) > 0
assert "descr" in str(base64.b64decode(readme_content)) assert "descr" in str(base64.b64decode(readme_content))
def test_create_file(instance): def test_create_file(instance):
TESTFILE_CONENTE = "TestStringFileContent" TESTFILE_CONENTE = "TestStringFileContent"
TESTFILE_CONENTE_B64 = base64.b64encode(bytes(TESTFILE_CONENTE, 'utf-8')) TESTFILE_CONENTE_B64 = base64.b64encode(bytes(TESTFILE_CONENTE, "utf-8"))
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = org.get_repository(test_repo) repo = org.get_repository(test_repo)
repo.create_file("testfile.md", repo.create_file("testfile.md", content=TESTFILE_CONENTE_B64.decode("ascii"))
content = TESTFILE_CONENTE_B64.decode("ascii"))
# test if putting was successful # test if putting was successful
content = repo.get_git_content() content = repo.get_git_content()
readmes = [c for c in content if c.name == "testfile.md"] readmes = [c for c in content if c.name == "testfile.md"]
@ -180,17 +186,19 @@ def test_create_file(instance):
assert len(readme_content) > 0 assert len(readme_content) > 0
assert TESTFILE_CONENTE in str(base64.b64decode(readme_content)) assert TESTFILE_CONENTE in str(base64.b64decode(readme_content))
def test_change_file(instance): def test_change_file(instance):
TESTFILE_CONENTE = "TestStringFileContent with changed content now" TESTFILE_CONENTE = "TestStringFileContent with changed content now"
TESTFILE_CONENTE_B64 = base64.b64encode(bytes(TESTFILE_CONENTE, 'utf-8')) TESTFILE_CONENTE_B64 = base64.b64encode(bytes(TESTFILE_CONENTE, "utf-8"))
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = org.get_repository(test_repo) repo = org.get_repository(test_repo)
# figure out the sha of the file to change # figure out the sha of the file to change
content = repo.get_git_content() content = repo.get_git_content()
readmes = [c for c in content if c.name == "testfile.md"] readmes = [c for c in content if c.name == "testfile.md"]
# change # change
repo.change_file("testfile.md", readmes[0].sha, repo.change_file(
content = TESTFILE_CONENTE_B64.decode("ascii")) "testfile.md", readmes[0].sha, content=TESTFILE_CONENTE_B64.decode("ascii")
)
# test if putting was successful # test if putting was successful
content = repo.get_git_content() content = repo.get_git_content()
readmes = [c for c in content if c.name == "testfile.md"] readmes = [c for c in content if c.name == "testfile.md"]
@ -199,6 +207,7 @@ def test_change_file(instance):
assert len(readme_content) > 0 assert len(readme_content) > 0
assert TESTFILE_CONENTE in str(base64.b64decode(readme_content)) assert TESTFILE_CONENTE in str(base64.b64decode(readme_content))
def test_create_branch(instance): def test_create_branch(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = org.get_repository(test_repo) repo = org.get_repository(test_repo)
@ -207,6 +216,7 @@ def test_create_branch(instance):
assert len(master) > 0 assert len(master) > 0
repo.add_branch(master[0], "test_branch") repo.add_branch(master[0], "test_branch")
def test_create_team(instance): def test_create_team(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
team = instance.create_team(org, test_team, "descr") team = instance.create_team(org, test_team, "descr")
@ -214,6 +224,7 @@ def test_create_team(instance):
assert team.description == "descr" assert team.description == "descr"
assert team.organization == org assert team.organization == org
def test_patch_team(instance): def test_patch_team(instance):
fields = { fields = {
"can_create_org_repo": True, "can_create_org_repo": True,
@ -238,13 +249,17 @@ def test_request_team(instance):
team2 = Team.request(instance, team.id) team2 = Team.request(instance, team.id)
assert team.name == team2.name assert team.name == team2.name
def test_create_milestone(instance): def test_create_milestone(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = org.get_repository(test_repo) repo = org.get_repository(test_repo)
ms = repo.create_milestone("I love this Milestone", "Find an otter to adopt this milestone") ms = repo.create_milestone(
"I love this Milestone", "Find an otter to adopt this milestone"
)
assert isinstance(ms, Milestone) assert isinstance(ms, Milestone)
assert ms.title == "I love this Milestone" assert ms.title == "I love this Milestone"
def test_user_teams(instance): def test_user_teams(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
team = org.get_team(test_team) team = org.get_team(test_team)
@ -253,11 +268,13 @@ def test_user_teams(instance):
teams = user.get_teams() teams = user.get_teams()
assert team in teams assert team in teams
def test_get_accessible_repositories(instance): def test_get_accessible_repositories(instance):
user = instance.get_user_by_name(test_user) user = instance.get_user_by_name(test_user)
repos = user.get_accessible_repos() repos = user.get_accessible_repos()
assert len(repos) > 0 assert len(repos) > 0
def test_create_issue(instance): def test_create_issue(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = Repository.request(instance, org.username, test_repo) repo = Repository.request(instance, org.username, test_repo)
@ -266,6 +283,7 @@ def test_create_issue(instance):
assert issue.title == "TestIssue" assert issue.title == "TestIssue"
assert issue.body == "Body text with this issue" assert issue.body == "Body text with this issue"
def test_hashing(instance): def test_hashing(instance):
# just call the hash function of each object to see if something bad happens # just call the hash function of each object to see if something bad happens
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
@ -284,7 +302,9 @@ def test_change_issue(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = org.get_repositories()[0] repo = org.get_repositories()[0]
ms_title = "othermilestone" ms_title = "othermilestone"
issue = Issue.create_issue(instance, repo, "IssueTestissue with Testinput", "asdf2332") issue = Issue.create_issue(
instance, repo, "IssueTestissue with Testinput", "asdf2332"
)
new_body = "some new description with some more of that char stuff :)" new_body = "some new description with some more of that char stuff :)"
issue.body = new_body issue.body = new_body
issue.commit() issue.commit()
@ -300,8 +320,17 @@ def test_change_issue(instance):
assert issue3.milestone is not None assert issue3.milestone is not None
assert issue3.milestone.description == "this is only a teststone2" assert issue3.milestone.description == "this is only a teststone2"
issues = repo.get_issues() issues = repo.get_issues()
assert len([issue for issue in issues assert (
if issue.milestone is not None and issue.milestone.title == ms_title]) > 0 len(
[
issue
for issue in issues
if issue.milestone is not None and issue.milestone.title == ms_title
]
)
> 0
)
def test_team_get_org(instance): def test_team_get_org(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
@ -309,6 +338,7 @@ def test_team_get_org(instance):
teams = user.get_teams() teams = user.get_teams()
assert org.username == teams[0].organization.name assert org.username == teams[0].organization.name
def test_delete_repo_userowned(instance): def test_delete_repo_userowned(instance):
user = User.request(instance, test_user) user = User.request(instance, test_user)
repo = Repository.request(instance, user.username, test_repo) repo = Repository.request(instance, user.username, test_repo)
@ -316,6 +346,7 @@ def test_delete_repo_userowned(instance):
with pytest.raises(NotFoundException) as e: with pytest.raises(NotFoundException) as e:
Repository.request(instance, test_user, test_repo) Repository.request(instance, test_user, test_repo)
def test_secundary_email(instance): def test_secundary_email(instance):
SECONDARYMAIL = "secondarytest@test.org" # set up with real email SECONDARYMAIL = "secondarytest@test.org" # set up with real email
sec_user = instance.get_user_by_email(SECONDARYMAIL) sec_user = instance.get_user_by_email(SECONDARYMAIL)
@ -334,7 +365,9 @@ def test_delete_repo_orgowned(instance):
def test_change_repo_ownership_org(instance): def test_change_repo_ownership_org(instance):
old_org = Organization.request(instance, test_org) old_org = Organization.request(instance, test_org)
user = User.request(instance, test_user) user = User.request(instance, test_user)
new_org = instance.create_org(user,test_org+"_repomove", "Org for testing moving repositories") new_org = instance.create_org(
user, test_org + "_repomove", "Org for testing moving repositories"
)
new_team = instance.create_team(new_org, test_team + "_repomove", "descr") new_team = instance.create_team(new_org, test_team + "_repomove", "descr")
repo_name = test_repo + "_repomove" repo_name = test_repo + "_repomove"
repo = instance.create_repo(old_org, repo_name, "descr") repo = instance.create_repo(old_org, repo_name, "descr")
@ -342,6 +375,7 @@ def test_change_repo_ownership_org(instance):
assert repo_name not in [repo.name for repo in old_org.get_repositories()] assert repo_name not in [repo.name for repo in old_org.get_repositories()]
assert repo_name in [repo.name for repo in new_org.get_repositories()] assert repo_name in [repo.name for repo in new_org.get_repositories()]
def test_change_repo_ownership_user(instance): def test_change_repo_ownership_user(instance):
old_org = Organization.request(instance, test_org) old_org = Organization.request(instance, test_org)
user = User.request(instance, test_user) user = User.request(instance, test_user)
@ -363,6 +397,7 @@ def test_delete_team(instance):
with pytest.raises(NotFoundException) as e: with pytest.raises(NotFoundException) as e:
team = org.get_team(test_team) team = org.get_team(test_team)
def test_delete_teams(instance): def test_delete_teams(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repos = org.get_repositories() repos = org.get_repositories()
@ -371,6 +406,7 @@ def test_delete_teams(instance):
repos = org.get_repositories() repos = org.get_repositories()
assert len(repos) == 0 assert len(repos) == 0
def test_delete_org(instance): def test_delete_org(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
org.delete() org.delete()

Wyświetl plik

@ -4,6 +4,7 @@ import uuid
from gitea import Gitea, User, Organization, Team, Repository, Issue from gitea import Gitea, User, Organization, Team, Repository, Issue
from gitea import NotFoundException, AlreadyExistsException from gitea import NotFoundException, AlreadyExistsException
# put a ".token" file into your directory containg only the token for gitea # put a ".token" file into your directory containg only the token for gitea
@pytest.fixture @pytest.fixture
def instance(scope="module"): def instance(scope="module"):
@ -20,15 +21,20 @@ def instance(scope="module"):
- Token at .token \ - Token at .token \
?" ?"
# make up some fresh names for the tests run # make up some fresh names for the tests run
test_org = "org_" + uuid.uuid4().hex[:8] test_org = "org_" + uuid.uuid4().hex[:8]
test_user = "user_" + uuid.uuid4().hex[:8] test_user = "user_" + uuid.uuid4().hex[:8]
test_team = "team_" + uuid.uuid4().hex[:8] # team names seem to have a rather low max lenght test_team = (
"team_" + uuid.uuid4().hex[:8]
) # team names seem to have a rather low max lenght
test_repo = "repo_" + uuid.uuid4().hex[:8] test_repo = "repo_" + uuid.uuid4().hex[:8]
def test_list_repos(instance): def test_list_repos(instance):
user = instance.create_user(test_user, test_user + "@example.org", "abcdefg1.23AB", send_notify=False) user = instance.create_user(
test_user, test_user + "@example.org", "abcdefg1.23AB", send_notify=False
)
org = instance.create_org(user, test_org, "some Description for longtests") org = instance.create_org(user, test_org, "some Description for longtests")
repos = org.get_repositories() repos = org.get_repositories()
assert len(repos) == 0 assert len(repos) == 0
@ -41,8 +47,15 @@ def test_list_repos(instance):
def test_list_issue(instance): def test_list_issue(instance):
org = Organization.request(instance, test_org) org = Organization.request(instance, test_org)
repo = instance.create_repo(org, test_repo, "Testing a huge number of Issues and how they are listed") repo = instance.create_repo(
org, test_repo, "Testing a huge number of Issues and how they are listed"
)
for x in range(0, 100): for x in range(0, 100):
Issue.create_issue(instance, repo, "TestIssue" + str(x), "We will be too many to be listed on one page") Issue.create_issue(
instance,
repo,
"TestIssue" + str(x),
"We will be too many to be listed on one page",
)
issues = repo.get_issues() issues = repo.get_issues()
assert len(issues) > 98 assert len(issues) > 98