kopia lustrzana https://github.com/Langenfeld/py-gitea
(style) reformatted with python black for easier merges
rodzic
3b8f04e31b
commit
9f6785cdee
10
README.md
10
README.md
|
@ -41,10 +41,11 @@ other_user = User.request(gitea, "OtherUserName")
|
|||
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 according object must be called to synchronize the changed fields with your gitea instance.
|
||||
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.
|
||||
|
||||
```python
|
||||
org = Organization.request(gitea, test_org)
|
||||
|
@ -54,11 +55,13 @@ org.commit()
|
|||
```
|
||||
|
||||
An entity in gitea can be deleted by calling delete.
|
||||
|
||||
```python
|
||||
org.delete()
|
||||
```
|
||||
|
||||
All entity objects do have methods to execute some of the requests possible though the gitea-api:
|
||||
|
||||
```python
|
||||
org = Organization.request(gitea, ORGNAME)
|
||||
teams = org.get_teams()
|
||||
|
@ -68,7 +71,6 @@ for team in teams:
|
|||
print(repo.name)
|
||||
```
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
Use ``pip install py-gitea`` to install.
|
||||
|
|
|
@ -15,21 +15,21 @@ from .apiobject import (
|
|||
Milestone,
|
||||
Commit,
|
||||
Comment,
|
||||
Content
|
||||
Content,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'Gitea',
|
||||
'User',
|
||||
'Organization',
|
||||
'Team',
|
||||
'Repository',
|
||||
'Branch',
|
||||
'NotFoundException',
|
||||
'AlreadyExistsException',
|
||||
'Issue',
|
||||
'Milestone',
|
||||
'Commit',
|
||||
'Comment',
|
||||
'Content'
|
||||
"Gitea",
|
||||
"User",
|
||||
"Organization",
|
||||
"Team",
|
||||
"Repository",
|
||||
"Branch",
|
||||
"NotFoundException",
|
||||
"AlreadyExistsException",
|
||||
"Issue",
|
||||
"Milestone",
|
||||
"Commit",
|
||||
"Comment",
|
||||
"Content",
|
||||
]
|
||||
|
|
|
@ -21,18 +21,19 @@ class Organization(ApiObject):
|
|||
super().__init__(gitea)
|
||||
|
||||
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
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.gitea) ^ hash(self.name)
|
||||
|
||||
@classmethod
|
||||
def request(cls, gitea: 'Gitea', name: str) -> 'Organization':
|
||||
def request(cls, gitea: "Gitea", name: str) -> "Organization":
|
||||
return cls._request(gitea, {"name": name})
|
||||
|
||||
@classmethod
|
||||
def parse_response(cls, gitea, result) -> 'Organization':
|
||||
def parse_response(cls, gitea, result) -> "Organization":
|
||||
api_object = super().parse_response(gitea, result)
|
||||
# add "name" field to make this behave similar to users for gitea < 1.18
|
||||
# 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)
|
||||
return api_object
|
||||
|
||||
_patchable_fields = {"description", "full_name", "location", "visibility", "website"}
|
||||
_patchable_fields = {
|
||||
"description",
|
||||
"full_name",
|
||||
"location",
|
||||
"visibility",
|
||||
"website",
|
||||
}
|
||||
|
||||
def commit(self):
|
||||
values = self.get_dirty_fields()
|
||||
args = {"name": self.name}
|
||||
self.gitea.requests_patch(
|
||||
Organization.API_OBJECT.format(**args), data=values
|
||||
)
|
||||
self.gitea.requests_patch(Organization.API_OBJECT.format(**args), data=values)
|
||||
self.dirty_fields = {}
|
||||
|
||||
def create_repo(
|
||||
|
@ -83,7 +88,9 @@ class Organization(ApiObject):
|
|||
},
|
||||
)
|
||||
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:
|
||||
self.gitea.logger.error(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]
|
||||
# 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
|
||||
|
||||
def get_team(self, name) -> "Team":
|
||||
|
@ -167,7 +175,8 @@ class User(ApiObject):
|
|||
self._emails = []
|
||||
|
||||
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
|
||||
|
||||
def __hash__(self):
|
||||
|
@ -179,7 +188,7 @@ class User(ApiObject):
|
|||
return self._emails
|
||||
|
||||
@classmethod
|
||||
def request(cls, gitea: 'Gitea', name: str) -> "User":
|
||||
def request(cls, gitea: "Gitea", name: str) -> "User":
|
||||
api_object = cls._request(gitea, {"name": name})
|
||||
return api_object
|
||||
|
||||
|
@ -249,7 +258,9 @@ class User(ApiObject):
|
|||
},
|
||||
)
|
||||
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:
|
||||
self.gitea.logger.error(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)
|
||||
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"
|
||||
results = self.gitea.requests_get_paginated(url, sudo=self)
|
||||
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."""
|
||||
results = self.gitea.requests_get("/user/repos", sudo=self)
|
||||
return [Repository.parse_response(self, result) for result in results]
|
||||
|
@ -300,7 +311,6 @@ class User(ApiObject):
|
|||
|
||||
|
||||
class Branch(ReadonlyApiObject):
|
||||
|
||||
def __init__(self, gitea):
|
||||
super().__init__(gitea)
|
||||
|
||||
|
@ -318,13 +328,15 @@ class Branch(ReadonlyApiObject):
|
|||
}
|
||||
|
||||
@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})
|
||||
|
||||
|
||||
class Repository(ApiObject):
|
||||
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_BRANCHES = """/repos/%s/%s/branches""" # <owner>, <reponame>
|
||||
REPO_ISSUES = """/repos/{owner}/{repo}/issues""" # <owner, reponame>
|
||||
|
@ -339,7 +351,8 @@ class Repository(ApiObject):
|
|||
super().__init__(gitea)
|
||||
|
||||
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
|
||||
|
||||
def __hash__(self):
|
||||
|
@ -348,12 +361,13 @@ class Repository(ApiObject):
|
|||
_fields_to_parsers = {
|
||||
# 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)
|
||||
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),
|
||||
}
|
||||
|
||||
@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})
|
||||
|
||||
_patchable_fields = {
|
||||
|
@ -391,7 +405,7 @@ class Repository(ApiObject):
|
|||
self.gitea.requests_patch(self.API_OBJECT.format(**args), data=values)
|
||||
self.dirty_fields = {}
|
||||
|
||||
def get_branches(self) -> List['Branch']:
|
||||
def get_branches(self) -> List["Branch"]:
|
||||
"""Get all the Branches of this Repository."""
|
||||
results = self.gitea.requests_get(
|
||||
Repository.REPO_BRANCHES % (self.owner.username, self.name)
|
||||
|
@ -431,7 +445,8 @@ class Repository(ApiObject):
|
|||
issues = []
|
||||
data = {"state": state}
|
||||
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:
|
||||
issue = Issue.parse_response(self.gitea, result)
|
||||
|
@ -467,14 +482,20 @@ class Repository(ApiObject):
|
|||
"title": title,
|
||||
}
|
||||
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)
|
||||
|
||||
def create_milestone(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)
|
||||
def create_milestone(
|
||||
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}
|
||||
if due_date: data["due_date"] = due_date
|
||||
if due_date:
|
||||
data["due_date"] = due_date
|
||||
result = self.gitea.requests_post(url, data=data)
|
||||
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}"
|
||||
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)
|
||||
data = {"new_owner": new_owner.username}
|
||||
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
|
||||
self.gitea.requests_post(url, data=data)
|
||||
# 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"""
|
||||
url = f"/repos/{self.owner.username}/{self.name}/contents"
|
||||
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
|
||||
|
||||
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"""
|
||||
url = f"/repos/{self.owner.username}/{self.name}/contents/{content.path}"
|
||||
data = {"ref": commit.sha} if commit else {}
|
||||
if content.type == Content.FILE:
|
||||
return self.gitea.requests_get(url, data)["content"]
|
||||
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):
|
||||
"""https://try.gitea.io/api/swagger#/repository/repoCreateFile"""
|
||||
|
@ -561,7 +596,9 @@ class Repository(ApiObject):
|
|||
data.update({"content": content})
|
||||
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"""
|
||||
if not data:
|
||||
data = {}
|
||||
|
@ -583,7 +620,8 @@ class Milestone(ApiObject):
|
|||
super().__init__(gitea)
|
||||
|
||||
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
|
||||
|
||||
def __hash__(self):
|
||||
|
@ -612,17 +650,17 @@ class Milestone(ApiObject):
|
|||
}
|
||||
|
||||
@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})
|
||||
|
||||
|
||||
class Comment(ApiObject):
|
||||
|
||||
def __init__(self, gitea):
|
||||
super().__init__(gitea)
|
||||
|
||||
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
|
||||
|
||||
def __hash__(self):
|
||||
|
@ -636,24 +674,26 @@ class Comment(ApiObject):
|
|||
|
||||
|
||||
class Commit(ReadonlyApiObject):
|
||||
|
||||
def __init__(self, gitea):
|
||||
super().__init__(gitea)
|
||||
|
||||
_fields_to_parsers = {
|
||||
# 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):
|
||||
if not isinstance(other, Commit): return False
|
||||
if not isinstance(other, Commit):
|
||||
return False
|
||||
return self.sha == other.sha
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.sha)
|
||||
|
||||
@classmethod
|
||||
def parse_response(cls, gitea, result) -> 'Commit':
|
||||
def parse_response(cls, gitea, result) -> "Commit":
|
||||
commit_cache = result["commit"]
|
||||
api_object = cls(gitea)
|
||||
cls._initialize(gitea, api_object, result)
|
||||
|
@ -675,7 +715,8 @@ class Issue(ApiObject):
|
|||
super().__init__(gitea)
|
||||
|
||||
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
|
||||
|
||||
def __hash__(self):
|
||||
|
@ -688,7 +729,7 @@ class Issue(ApiObject):
|
|||
"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,
|
||||
# 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 = {
|
||||
|
@ -707,13 +748,19 @@ class Issue(ApiObject):
|
|||
|
||||
def commit(self):
|
||||
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.dirty_fields = {}
|
||||
|
||||
@classmethod
|
||||
def request(cls, gitea: 'Gitea', owner: str, repo: str, number: str):
|
||||
api_object = cls._request(gitea, {"owner": owner, "repo": repo, "index": number})
|
||||
def request(cls, gitea: "Gitea", owner: str, repo: str, number: str):
|
||||
api_object = cls._request(
|
||||
gitea, {"owner": owner, "repo": repo, "index": number}
|
||||
)
|
||||
return api_object
|
||||
|
||||
@classmethod
|
||||
|
@ -774,7 +821,8 @@ class Team(ApiObject):
|
|||
super().__init__(gitea)
|
||||
|
||||
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
|
||||
|
||||
def __hash__(self):
|
||||
|
@ -838,8 +886,11 @@ class Content(ReadonlyApiObject):
|
|||
super().__init__(gitea)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Team): return False
|
||||
return self.repo == self.repo and self.sha == other.sha and self.name == other.name
|
||||
if not isinstance(other, Team):
|
||||
return False
|
||||
return (
|
||||
self.repo == self.repo and self.sha == other.sha and self.name == other.name
|
||||
)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.repo) ^ hash(self.sha) ^ hash(self.name)
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
from .exceptions import ObjectIsInvalid, MissiongEqualyImplementation, RawRequestEndpointMissing
|
||||
from .exceptions import (
|
||||
ObjectIsInvalid,
|
||||
MissiongEqualyImplementation,
|
||||
RawRequestEndpointMissing,
|
||||
)
|
||||
|
||||
|
||||
class ReadonlyApiObject:
|
||||
|
||||
def __init__(self, gitea):
|
||||
self.gitea = gitea
|
||||
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):
|
||||
if not hasattr(api_object, name):
|
||||
setattr(api_object, "_" + name, value)
|
||||
prop = property(
|
||||
(lambda n: lambda self: self._get_var(n))(name))
|
||||
prop = property((lambda n: lambda self: self._get_var(n))(name))
|
||||
setattr(cls, name, prop)
|
||||
else:
|
||||
raise AttributeError(f"Attribute {name} already exists on api object.")
|
||||
|
@ -107,7 +109,8 @@ class ApiObject(ReadonlyApiObject):
|
|||
setattr(api_object, "_" + name, value)
|
||||
prop = property(
|
||||
(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)
|
||||
|
||||
def __set_var(self, name, i):
|
||||
|
|
|
@ -17,6 +17,7 @@ class ConflictException(Exception):
|
|||
class RawRequestEndpointMissing(Exception):
|
||||
"""This ApiObject can only be obtained through other api objects and does not have
|
||||
diret .request method."""
|
||||
|
||||
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
|
||||
fields obtained from gitea. Risen if an api object is lacking the proper implementation.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
|
|
@ -12,6 +12,7 @@ from .exceptions import NotFoundException, ConflictException, AlreadyExistsExcep
|
|||
|
||||
class Gitea:
|
||||
"""Object to establish a session with Gitea."""
|
||||
|
||||
ADMIN_CREATE_USER = """/admin/users"""
|
||||
GET_USERS_ADMIN = """/admin/users"""
|
||||
ADMIN_REPO_CREATE = """/admin/users/%s/repos""" # <ownername>
|
||||
|
@ -21,12 +22,7 @@ class Gitea:
|
|||
CREATE_TEAM = """/orgs/%s/teams""" # <orgname>
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
gitea_url: str,
|
||||
token_text=None,
|
||||
auth=None,
|
||||
verify=True,
|
||||
log_level="INFO"
|
||||
self, gitea_url: str, token_text=None, auth=None, verify=True, log_level="INFO"
|
||||
):
|
||||
"""Initializing Gitea-instance
|
||||
|
||||
|
@ -60,7 +56,6 @@ class Gitea:
|
|||
if not verify:
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
|
||||
def __get_url(self, endpoint):
|
||||
url = self.url + "/api/v1" + endpoint
|
||||
self.logger.debug("Url: %s" % url)
|
||||
|
@ -78,19 +73,25 @@ class Gitea:
|
|||
combined_params.update(params)
|
||||
if sudo:
|
||||
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]:
|
||||
message = f"Received status code: {request.status_code} ({request.url})"
|
||||
if request.status_code in [404]:
|
||||
raise NotFoundException(message)
|
||||
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]:
|
||||
raise ConflictException(message)
|
||||
raise Exception(message)
|
||||
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
|
||||
combined_params = {}
|
||||
combined_params.update(params)
|
||||
|
@ -106,7 +107,9 @@ class Gitea:
|
|||
def requests_put(self, endpoint: str, data: dict = None):
|
||||
if not 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]:
|
||||
message = f"Received status code: {request.status_code} ({request.url}) {request.text}"
|
||||
self.logger.error(message)
|
||||
|
@ -120,21 +123,34 @@ class Gitea:
|
|||
raise Exception(message)
|
||||
|
||||
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 ("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)
|
||||
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"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)
|
||||
|
||||
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]:
|
||||
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)
|
||||
raise Exception(error_message)
|
||||
return self.parse_result(request)
|
||||
|
|
34
setup.py
34
setup.py
|
@ -1,35 +1,31 @@
|
|||
from setuptools import setup, find_packages
|
||||
|
||||
with open('README.md') as readme_file:
|
||||
with open("README.md") as readme_file:
|
||||
README = readme_file.read()
|
||||
|
||||
setup_args = dict(
|
||||
name='py-gitea',
|
||||
version='0.2.6',
|
||||
description='A python wrapper for the Gitea API',
|
||||
name="py-gitea",
|
||||
version="0.2.6",
|
||||
description="A python wrapper for the Gitea API",
|
||||
long_description_content_type="text/markdown",
|
||||
long_description=README,
|
||||
license='MIT',
|
||||
license="MIT",
|
||||
packages=find_packages(),
|
||||
author='Vincent Langenfeld ',
|
||||
author_email='langenfv@tf.uni-freiburg.de',
|
||||
keywords=['Gitea','api','wrapper'],
|
||||
url='https://github.com/Langenfeld/py-gitea',
|
||||
download_url='https://pypi.org/project/py-gitea/'
|
||||
author="Vincent Langenfeld ",
|
||||
author_email="langenfv@tf.uni-freiburg.de",
|
||||
keywords=["Gitea", "api", "wrapper"],
|
||||
url="https://github.com/Langenfeld/py-gitea",
|
||||
download_url="https://pypi.org/project/py-gitea/",
|
||||
)
|
||||
|
||||
install_requires = [
|
||||
'requests',
|
||||
'frozendict',
|
||||
"requests",
|
||||
"frozendict",
|
||||
]
|
||||
|
||||
extras_require = {
|
||||
'test': ['pytest']
|
||||
}
|
||||
extras_require = {"test": ["pytest"]}
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
setup(
|
||||
**setup_args,
|
||||
install_requires=install_requires,
|
||||
extras_require=extras_require
|
||||
**setup_args, install_requires=install_requires, extras_require=extras_require
|
||||
)
|
||||
|
|
|
@ -13,6 +13,7 @@ import pytest
|
|||
|
||||
from gitea import Gitea
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def instance(scope="module"):
|
||||
try:
|
||||
|
|
|
@ -6,6 +6,7 @@ import uuid
|
|||
from gitea import Gitea, User, Organization, Team, Repository, Issue, Milestone
|
||||
from gitea import NotFoundException, AlreadyExistsException
|
||||
|
||||
|
||||
# put a ".token" file into your directory containg only the token for gitea
|
||||
@pytest.fixture
|
||||
def instance(scope="module"):
|
||||
|
@ -22,10 +23,13 @@ def instance(scope="module"):
|
|||
- Token at .token \
|
||||
?"
|
||||
|
||||
|
||||
# make up some fresh names for the tests run
|
||||
test_org = "org_" + 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]
|
||||
|
||||
|
||||
|
@ -65,6 +69,7 @@ def test_create_user(instance):
|
|||
assert type(user.id) is int
|
||||
assert user.is_admin is False
|
||||
|
||||
|
||||
def test_change_user(instance):
|
||||
user = instance.get_user_by_name(test_user)
|
||||
location = "a house"
|
||||
|
@ -72,7 +77,7 @@ def test_change_user(instance):
|
|||
new_fullname = "Other Test Full Name"
|
||||
user.full_name = new_fullname
|
||||
user.commit(user.username, 0)
|
||||
del(user)
|
||||
del user
|
||||
user = instance.get_user_by_name(test_user)
|
||||
assert user.full_name == new_fullname
|
||||
assert user.location == location
|
||||
|
@ -153,6 +158,7 @@ def test_list_branches(instance):
|
|||
master = [b for b in branches if b.name == "master"]
|
||||
assert len(master) > 0
|
||||
|
||||
|
||||
def test_list_files_and_content(instance):
|
||||
org = Organization.request(instance, test_org)
|
||||
repo = org.get_repository(test_repo)
|
||||
|
@ -165,13 +171,13 @@ def test_list_files_and_content(instance):
|
|||
assert len(readme_content) > 0
|
||||
assert "descr" in str(base64.b64decode(readme_content))
|
||||
|
||||
|
||||
def test_create_file(instance):
|
||||
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)
|
||||
repo = org.get_repository(test_repo)
|
||||
repo.create_file("testfile.md",
|
||||
content = TESTFILE_CONENTE_B64.decode("ascii"))
|
||||
repo.create_file("testfile.md", content=TESTFILE_CONENTE_B64.decode("ascii"))
|
||||
# test if putting was successful
|
||||
content = repo.get_git_content()
|
||||
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 TESTFILE_CONENTE in str(base64.b64decode(readme_content))
|
||||
|
||||
|
||||
def test_change_file(instance):
|
||||
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)
|
||||
repo = org.get_repository(test_repo)
|
||||
# figure out the sha of the file to change
|
||||
content = repo.get_git_content()
|
||||
readmes = [c for c in content if c.name == "testfile.md"]
|
||||
# change
|
||||
repo.change_file("testfile.md", readmes[0].sha,
|
||||
content = TESTFILE_CONENTE_B64.decode("ascii"))
|
||||
repo.change_file(
|
||||
"testfile.md", readmes[0].sha, content=TESTFILE_CONENTE_B64.decode("ascii")
|
||||
)
|
||||
# test if putting was successful
|
||||
content = repo.get_git_content()
|
||||
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 TESTFILE_CONENTE in str(base64.b64decode(readme_content))
|
||||
|
||||
|
||||
def test_create_branch(instance):
|
||||
org = Organization.request(instance, test_org)
|
||||
repo = org.get_repository(test_repo)
|
||||
|
@ -207,6 +216,7 @@ def test_create_branch(instance):
|
|||
assert len(master) > 0
|
||||
repo.add_branch(master[0], "test_branch")
|
||||
|
||||
|
||||
def test_create_team(instance):
|
||||
org = Organization.request(instance, test_org)
|
||||
team = instance.create_team(org, test_team, "descr")
|
||||
|
@ -214,6 +224,7 @@ def test_create_team(instance):
|
|||
assert team.description == "descr"
|
||||
assert team.organization == org
|
||||
|
||||
|
||||
def test_patch_team(instance):
|
||||
fields = {
|
||||
"can_create_org_repo": True,
|
||||
|
@ -238,13 +249,17 @@ def test_request_team(instance):
|
|||
team2 = Team.request(instance, team.id)
|
||||
assert team.name == team2.name
|
||||
|
||||
|
||||
def test_create_milestone(instance):
|
||||
org = Organization.request(instance, test_org)
|
||||
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 ms.title == "I love this Milestone"
|
||||
|
||||
|
||||
def test_user_teams(instance):
|
||||
org = Organization.request(instance, test_org)
|
||||
team = org.get_team(test_team)
|
||||
|
@ -253,11 +268,13 @@ def test_user_teams(instance):
|
|||
teams = user.get_teams()
|
||||
assert team in teams
|
||||
|
||||
|
||||
def test_get_accessible_repositories(instance):
|
||||
user = instance.get_user_by_name(test_user)
|
||||
repos = user.get_accessible_repos()
|
||||
assert len(repos) > 0
|
||||
|
||||
|
||||
def test_create_issue(instance):
|
||||
org = Organization.request(instance, test_org)
|
||||
repo = Repository.request(instance, org.username, test_repo)
|
||||
|
@ -266,6 +283,7 @@ def test_create_issue(instance):
|
|||
assert issue.title == "TestIssue"
|
||||
assert issue.body == "Body text with this issue"
|
||||
|
||||
|
||||
def test_hashing(instance):
|
||||
# just call the hash function of each object to see if something bad happens
|
||||
org = Organization.request(instance, test_org)
|
||||
|
@ -284,7 +302,9 @@ def test_change_issue(instance):
|
|||
org = Organization.request(instance, test_org)
|
||||
repo = org.get_repositories()[0]
|
||||
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 :)"
|
||||
issue.body = new_body
|
||||
issue.commit()
|
||||
|
@ -300,8 +320,17 @@ def test_change_issue(instance):
|
|||
assert issue3.milestone is not None
|
||||
assert issue3.milestone.description == "this is only a teststone2"
|
||||
issues = repo.get_issues()
|
||||
assert len([issue for issue in issues
|
||||
if issue.milestone is not None and issue.milestone.title == ms_title]) > 0
|
||||
assert (
|
||||
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):
|
||||
org = Organization.request(instance, test_org)
|
||||
|
@ -309,6 +338,7 @@ def test_team_get_org(instance):
|
|||
teams = user.get_teams()
|
||||
assert org.username == teams[0].organization.name
|
||||
|
||||
|
||||
def test_delete_repo_userowned(instance):
|
||||
user = User.request(instance, test_user)
|
||||
repo = Repository.request(instance, user.username, test_repo)
|
||||
|
@ -316,6 +346,7 @@ def test_delete_repo_userowned(instance):
|
|||
with pytest.raises(NotFoundException) as e:
|
||||
Repository.request(instance, test_user, test_repo)
|
||||
|
||||
|
||||
def test_secundary_email(instance):
|
||||
SECONDARYMAIL = "secondarytest@test.org" # set up with real email
|
||||
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):
|
||||
old_org = Organization.request(instance, test_org)
|
||||
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")
|
||||
repo_name = test_repo + "_repomove"
|
||||
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 in [repo.name for repo in new_org.get_repositories()]
|
||||
|
||||
|
||||
def test_change_repo_ownership_user(instance):
|
||||
old_org = Organization.request(instance, test_org)
|
||||
user = User.request(instance, test_user)
|
||||
|
@ -363,6 +397,7 @@ def test_delete_team(instance):
|
|||
with pytest.raises(NotFoundException) as e:
|
||||
team = org.get_team(test_team)
|
||||
|
||||
|
||||
def test_delete_teams(instance):
|
||||
org = Organization.request(instance, test_org)
|
||||
repos = org.get_repositories()
|
||||
|
@ -371,6 +406,7 @@ def test_delete_teams(instance):
|
|||
repos = org.get_repositories()
|
||||
assert len(repos) == 0
|
||||
|
||||
|
||||
def test_delete_org(instance):
|
||||
org = Organization.request(instance, test_org)
|
||||
org.delete()
|
||||
|
|
|
@ -4,6 +4,7 @@ import uuid
|
|||
from gitea import Gitea, User, Organization, Team, Repository, Issue
|
||||
from gitea import NotFoundException, AlreadyExistsException
|
||||
|
||||
|
||||
# put a ".token" file into your directory containg only the token for gitea
|
||||
@pytest.fixture
|
||||
def instance(scope="module"):
|
||||
|
@ -20,15 +21,20 @@ def instance(scope="module"):
|
|||
- Token at .token \
|
||||
?"
|
||||
|
||||
|
||||
# make up some fresh names for the tests run
|
||||
test_org = "org_" + 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]
|
||||
|
||||
|
||||
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")
|
||||
repos = org.get_repositories()
|
||||
assert len(repos) == 0
|
||||
|
@ -41,8 +47,15 @@ def test_list_repos(instance):
|
|||
|
||||
def test_list_issue(instance):
|
||||
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):
|
||||
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()
|
||||
assert len(issues) > 98
|
||||
|
|
Ładowanie…
Reference in New Issue