python-gitea 0.12.0__py3-none-any.whl → 0.12.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gitea/cli/issue/label/common.py +67 -17
- gitea/label/async_label.py +57 -0
- gitea/label/base.py +39 -0
- gitea/label/label.py +57 -0
- {python_gitea-0.12.0.dist-info → python_gitea-0.12.2.dist-info}/METADATA +2 -2
- {python_gitea-0.12.0.dist-info → python_gitea-0.12.2.dist-info}/RECORD +9 -9
- {python_gitea-0.12.0.dist-info → python_gitea-0.12.2.dist-info}/WHEEL +0 -0
- {python_gitea-0.12.0.dist-info → python_gitea-0.12.2.dist-info}/entry_points.txt +0 -0
- {python_gitea-0.12.0.dist-info → python_gitea-0.12.2.dist-info}/licenses/LICENSE +0 -0
gitea/cli/issue/label/common.py
CHANGED
|
@@ -7,9 +7,13 @@ and one that does not rejects the whole request. Resolving first also means an
|
|
|
7
7
|
unknown name is an error before anything is written, rather than a label silently
|
|
8
8
|
not applied, and before the first of several issues has been changed.
|
|
9
9
|
|
|
10
|
-
A value made only of ASCII digits is taken to be an ID
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
A value made only of ASCII digits is taken to be an ID. An ID is checked too,
|
|
11
|
+
against the repository's labels and then, for one not among them, against the
|
|
12
|
+
labels of the organization owning the repository, which the repository's listing
|
|
13
|
+
does not include. An ID found in neither is an error before anything is written:
|
|
14
|
+
Gitea drops an ID it cannot apply without saying so, so an ID copied from another
|
|
15
|
+
repository would otherwise be a silent no-op for `add`, and for `set` would leave
|
|
16
|
+
the issue with no labels at all.
|
|
13
17
|
|
|
14
18
|
Every command takes one `--issue-id` or several. With one, `data` is that issue's
|
|
15
19
|
labels, as `gitea-cli issue label list` emits them. With several, `data` holds one
|
|
@@ -25,6 +29,8 @@ import re
|
|
|
25
29
|
from collections.abc import Callable
|
|
26
30
|
from typing import TYPE_CHECKING, Any
|
|
27
31
|
|
|
32
|
+
from requests import HTTPError
|
|
33
|
+
|
|
28
34
|
from gitea.cli.utils.errors import CommandError
|
|
29
35
|
from gitea.utils.pagination import PAGE_SIZE, collect_all_pages
|
|
30
36
|
|
|
@@ -37,6 +43,7 @@ ISSUE_IDS_HELP = "Issue number shown in the web UI. Repeat the option to act on
|
|
|
37
43
|
LABEL_HELP = "Label name or numeric ID. Repeat the option for several labels."
|
|
38
44
|
|
|
39
45
|
_NUMERIC_ID = re.compile(r"[0-9]+")
|
|
46
|
+
_NOT_FOUND = 404
|
|
40
47
|
|
|
41
48
|
|
|
42
49
|
def require_issue_ids(issue_ids: list[int] | None, *, command: str) -> list[int]:
|
|
@@ -79,14 +86,41 @@ def require_labels(labels: list[str] | None, *, command: str, hint: str = "") ->
|
|
|
79
86
|
return labels
|
|
80
87
|
|
|
81
88
|
|
|
89
|
+
def _organization_label_ids(client: Gitea, owner: str) -> set[int]:
|
|
90
|
+
"""Read the IDs of the labels defined on the organization owning a repository.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
client: The client to list the labels with.
|
|
94
|
+
owner: The owner of the repository.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
The IDs, or none when the owner is a user rather than an organization,
|
|
98
|
+
which Gitea answers with a 404.
|
|
99
|
+
|
|
100
|
+
Raises:
|
|
101
|
+
HTTPError: If the listing failed for any other reason.
|
|
102
|
+
|
|
103
|
+
"""
|
|
104
|
+
try:
|
|
105
|
+
defined, _ = collect_all_pages(
|
|
106
|
+
lambda page: client.label.list_organization_labels(organization=owner, page=page, limit=PAGE_SIZE)
|
|
107
|
+
)
|
|
108
|
+
except HTTPError as e:
|
|
109
|
+
response = getattr(e, "response", None)
|
|
110
|
+
if response is None or response.status_code != _NOT_FOUND:
|
|
111
|
+
raise
|
|
112
|
+
return set()
|
|
113
|
+
return {label["id"] for label in defined}
|
|
114
|
+
|
|
115
|
+
|
|
82
116
|
def resolve_label_ids(client: Gitea, owner: str, repository: str, labels: list[str], *, command: str) -> list[int]:
|
|
83
|
-
"""Resolve the labels given on the command line to their IDs.
|
|
117
|
+
"""Resolve the labels given on the command line to their IDs, and check that each exists.
|
|
84
118
|
|
|
85
|
-
The repository's labels are listed
|
|
86
|
-
|
|
119
|
+
The repository's labels are listed once for all of them. The organization's
|
|
120
|
+
are listed only when an ID is not among the repository's.
|
|
87
121
|
|
|
88
122
|
Args:
|
|
89
|
-
client: The client to list the
|
|
123
|
+
client: The client to list the labels with.
|
|
90
124
|
owner: The owner of the repository.
|
|
91
125
|
repository: The name of the repository.
|
|
92
126
|
labels: The labels, each a name or a numeric ID.
|
|
@@ -96,18 +130,19 @@ def resolve_label_ids(client: Gitea, owner: str, repository: str, labels: list[s
|
|
|
96
130
|
The label IDs, in the order given, each once.
|
|
97
131
|
|
|
98
132
|
Raises:
|
|
99
|
-
CommandError: If a name matches no label of the repository, or more than
|
|
133
|
+
CommandError: If a name matches no label of the repository, or more than
|
|
134
|
+
one, or an ID is neither a label of the repository nor of the
|
|
135
|
+
organization owning it.
|
|
100
136
|
|
|
101
137
|
"""
|
|
102
|
-
|
|
138
|
+
defined, _ = collect_all_pages(
|
|
139
|
+
lambda page: client.label.list_labels(owner=owner, repository=repository, page=page, limit=PAGE_SIZE)
|
|
140
|
+
)
|
|
103
141
|
by_name: dict[str, list[int]] = {}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
lambda page: client.label.list_labels(owner=owner, repository=repository, page=page, limit=PAGE_SIZE)
|
|
107
|
-
)
|
|
108
|
-
for label in defined:
|
|
109
|
-
by_name.setdefault(label["name"], []).append(label["id"])
|
|
142
|
+
for label in defined:
|
|
143
|
+
by_name.setdefault(label["name"], []).append(label["id"])
|
|
110
144
|
|
|
145
|
+
names = [label for label in labels if not _NUMERIC_ID.fullmatch(label)]
|
|
111
146
|
unknown = [name for name in names if name not in by_name]
|
|
112
147
|
if unknown:
|
|
113
148
|
raise CommandError(
|
|
@@ -122,8 +157,23 @@ def resolve_label_ids(client: Gitea, owner: str, repository: str, labels: list[s
|
|
|
122
157
|
f"in {owner}/{repository}. Pass the numeric ID of the one you mean."
|
|
123
158
|
)
|
|
124
159
|
|
|
125
|
-
|
|
126
|
-
|
|
160
|
+
repository_ids = {label["id"] for label in defined}
|
|
161
|
+
ids = [int(label) for label in labels if _NUMERIC_ID.fullmatch(label)]
|
|
162
|
+
elsewhere = [label_id for label_id in dict.fromkeys(ids) if label_id not in repository_ids]
|
|
163
|
+
if elsewhere:
|
|
164
|
+
organization_ids = _organization_label_ids(client, owner)
|
|
165
|
+
missing = [label_id for label_id in elsewhere if label_id not in organization_ids]
|
|
166
|
+
if missing:
|
|
167
|
+
raise CommandError(
|
|
168
|
+
f"'{command}': no label with ID {', '.join(str(label_id) for label_id in missing)} "
|
|
169
|
+
f"in {owner}/{repository} or on its owner {owner}. "
|
|
170
|
+
f"A label's ID belongs to the repository or organization defining it, so an ID read from "
|
|
171
|
+
f"another repository names a different label there, or none; nothing was changed. "
|
|
172
|
+
f"Pass the label's name instead; 'gitea-cli label list' shows the repository's labels."
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
resolved = [int(label) if _NUMERIC_ID.fullmatch(label) else by_name[label][0] for label in labels]
|
|
176
|
+
return list(dict.fromkeys(resolved))
|
|
127
177
|
|
|
128
178
|
|
|
129
179
|
def for_each_issue(
|
gitea/label/async_label.py
CHANGED
|
@@ -74,6 +74,63 @@ class AsyncLabel(BaseLabel, AsyncResource):
|
|
|
74
74
|
data, status_code = await process_async_response(response, default=[])
|
|
75
75
|
return cast(list[dict[str, Any]], data), {"status_code": status_code}
|
|
76
76
|
|
|
77
|
+
async def _list_organization_labels(
|
|
78
|
+
self,
|
|
79
|
+
organization: str,
|
|
80
|
+
page: int | None = None,
|
|
81
|
+
limit: int | None = None,
|
|
82
|
+
**kwargs: Any,
|
|
83
|
+
) -> ClientResponse:
|
|
84
|
+
"""List the labels defined on an organization.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
organization: The name of the organization.
|
|
88
|
+
page: The page number for pagination.
|
|
89
|
+
limit: The number of labels per page.
|
|
90
|
+
**kwargs: Additional arguments for the request.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
The HTTP response object.
|
|
94
|
+
|
|
95
|
+
"""
|
|
96
|
+
endpoint, params = self._list_organization_labels_helper(
|
|
97
|
+
organization=organization,
|
|
98
|
+
page=page,
|
|
99
|
+
limit=limit,
|
|
100
|
+
)
|
|
101
|
+
return await self._get(endpoint=endpoint, params=params, **kwargs)
|
|
102
|
+
|
|
103
|
+
async def list_organization_labels(
|
|
104
|
+
self,
|
|
105
|
+
organization: str,
|
|
106
|
+
page: int | None = None,
|
|
107
|
+
limit: int | None = None,
|
|
108
|
+
**kwargs: Any,
|
|
109
|
+
) -> tuple[list[dict[str, Any]], dict[str, Any]]:
|
|
110
|
+
"""List the labels defined on an organization.
|
|
111
|
+
|
|
112
|
+
These are the labels every repository of the organization can use, and
|
|
113
|
+
which the listing of a repository's own labels does not include.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
organization: The name of the organization.
|
|
117
|
+
page: The page number for pagination.
|
|
118
|
+
limit: The number of labels per page.
|
|
119
|
+
**kwargs: Additional arguments for the request.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
A tuple containing a list of labels as dictionaries and a dictionary with metadata.
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
response = await self._list_organization_labels(
|
|
126
|
+
organization=organization,
|
|
127
|
+
page=page,
|
|
128
|
+
limit=limit,
|
|
129
|
+
**kwargs,
|
|
130
|
+
)
|
|
131
|
+
data, status_code = await process_async_response(response, default=[])
|
|
132
|
+
return cast(list[dict[str, Any]], data), {"status_code": status_code}
|
|
133
|
+
|
|
77
134
|
async def _create_label(
|
|
78
135
|
self,
|
|
79
136
|
owner: str,
|
gitea/label/base.py
CHANGED
|
@@ -50,6 +50,45 @@ class BaseLabel:
|
|
|
50
50
|
|
|
51
51
|
return endpoint, params
|
|
52
52
|
|
|
53
|
+
def _list_organization_labels_endpoint(self, organization: str) -> str:
|
|
54
|
+
"""Construct the endpoint URL for listing the labels defined on an organization.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
organization: The name of the organization.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
The endpoint URL for listing the organization's labels.
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
return f"/orgs/{organization}/labels"
|
|
64
|
+
|
|
65
|
+
def _list_organization_labels_helper(
|
|
66
|
+
self,
|
|
67
|
+
organization: str,
|
|
68
|
+
page: int | None = None,
|
|
69
|
+
limit: int | None = None,
|
|
70
|
+
) -> tuple[str, dict[str, Any]]:
|
|
71
|
+
"""Get the endpoint and parameters for listing the labels defined on an organization.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
organization: The name of the organization.
|
|
75
|
+
page: The page number for pagination.
|
|
76
|
+
limit: The number of labels per page.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
A tuple containing the endpoint and the request arguments.
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
endpoint = self._list_organization_labels_endpoint(organization=organization)
|
|
83
|
+
|
|
84
|
+
params = {}
|
|
85
|
+
if page is not None:
|
|
86
|
+
params["page"] = page
|
|
87
|
+
if limit is not None:
|
|
88
|
+
params["limit"] = limit
|
|
89
|
+
|
|
90
|
+
return endpoint, params
|
|
91
|
+
|
|
53
92
|
def _create_label_endpoint(self, owner: str, repository: str) -> str:
|
|
54
93
|
"""Construct the endpoint URL for creating a label in a repository.
|
|
55
94
|
|
gitea/label/label.py
CHANGED
|
@@ -74,6 +74,63 @@ class Label(BaseLabel, Resource):
|
|
|
74
74
|
data, status_code = process_response(response, default=[])
|
|
75
75
|
return cast(list[dict[str, Any]], data), {"status_code": status_code}
|
|
76
76
|
|
|
77
|
+
def _list_organization_labels(
|
|
78
|
+
self,
|
|
79
|
+
organization: str,
|
|
80
|
+
page: int | None = None,
|
|
81
|
+
limit: int | None = None,
|
|
82
|
+
**kwargs: Any,
|
|
83
|
+
) -> Response:
|
|
84
|
+
"""List the labels defined on an organization.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
organization: The name of the organization.
|
|
88
|
+
page: The page number for pagination.
|
|
89
|
+
limit: The number of labels per page.
|
|
90
|
+
**kwargs: Additional arguments for the request.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
The HTTP response object.
|
|
94
|
+
|
|
95
|
+
"""
|
|
96
|
+
endpoint, params = self._list_organization_labels_helper(
|
|
97
|
+
organization=organization,
|
|
98
|
+
page=page,
|
|
99
|
+
limit=limit,
|
|
100
|
+
)
|
|
101
|
+
return self._get(endpoint=endpoint, params=params, **kwargs)
|
|
102
|
+
|
|
103
|
+
def list_organization_labels(
|
|
104
|
+
self,
|
|
105
|
+
organization: str,
|
|
106
|
+
page: int | None = None,
|
|
107
|
+
limit: int | None = None,
|
|
108
|
+
**kwargs: Any,
|
|
109
|
+
) -> tuple[list[dict[str, Any]], dict[str, Any]]:
|
|
110
|
+
"""List the labels defined on an organization.
|
|
111
|
+
|
|
112
|
+
These are the labels every repository of the organization can use, and
|
|
113
|
+
which the listing of a repository's own labels does not include.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
organization: The name of the organization.
|
|
117
|
+
page: The page number for pagination.
|
|
118
|
+
limit: The number of labels per page.
|
|
119
|
+
**kwargs: Additional arguments for the request.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
A tuple containing a list of labels as dictionaries and a dictionary with metadata.
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
response = self._list_organization_labels(
|
|
126
|
+
organization=organization,
|
|
127
|
+
page=page,
|
|
128
|
+
limit=limit,
|
|
129
|
+
**kwargs,
|
|
130
|
+
)
|
|
131
|
+
data, status_code = process_response(response, default=[])
|
|
132
|
+
return cast(list[dict[str, Any]], data), {"status_code": status_code}
|
|
133
|
+
|
|
77
134
|
def _create_label(
|
|
78
135
|
self,
|
|
79
136
|
owner: str,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: python-gitea
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.2
|
|
4
4
|
Summary: A Python package for interacting with the Gitea API, offering a simple interface to access repositories, users, organizations, issues, and more for automation and data management.
|
|
5
5
|
Project-URL: Documentation, https://isaac-cf-wong.github.io/python-gitea
|
|
6
6
|
Project-URL: Source, https://github.com/isaac-cf-wong/python-gitea
|
|
@@ -18,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.14
|
|
19
19
|
Requires-Python: >=3.12
|
|
20
20
|
Requires-Dist: aiohttp>=3.13.3
|
|
21
|
-
Requires-Dist: platformdirs>=4.
|
|
21
|
+
Requires-Dist: platformdirs>=4.4.0
|
|
22
22
|
Requires-Dist: pydantic>=2.12.5
|
|
23
23
|
Requires-Dist: pyyaml>=6.0.2
|
|
24
24
|
Requires-Dist: requests>=2.33.0
|
|
@@ -89,7 +89,7 @@ gitea/cli/issue/dependency/remove.py,sha256=1Adml7ATeO7RoOOYb6ZR9K1qBpbqgVXfW1F0
|
|
|
89
89
|
gitea/cli/issue/label/__init__.py,sha256=og5McB5fVl_BTSBBNGnYad_NAiy_M6-Djg2LtyUYzxo,54
|
|
90
90
|
gitea/cli/issue/label/add.py,sha256=N75TrExobdWf_LdPQdyoiyc_jcQjBjvg3OL2YSelP2Q,3652
|
|
91
91
|
gitea/cli/issue/label/clear.py,sha256=dGPlT4neZt454XlnzdIZOzJgs8IckaqlpaRT0uhM8g0,3500
|
|
92
|
-
gitea/cli/issue/label/common.py,sha256=
|
|
92
|
+
gitea/cli/issue/label/common.py,sha256=Q9LC0MhYF0AIm2ddKVCloLfEE-JjOz5RfayxnDPwSps,11237
|
|
93
93
|
gitea/cli/issue/label/list.py,sha256=GqSkZjRXn7-wAWVa65cajV3uLxh63IT00R2VFM2nQR4,3127
|
|
94
94
|
gitea/cli/issue/label/main.py,sha256=vLSjxNpP8ymLwIEUxrOPnYciUOmdwb8Jo1o_PersxLc,1179
|
|
95
95
|
gitea/cli/issue/label/remove.py,sha256=AIW1_Rzz444An-iuMM7bPH2kI6SFmLy_JkEY3F7Y8ds,3644
|
|
@@ -167,9 +167,9 @@ gitea/issue/base.py,sha256=Q13QLwxKGozvmd7oPiex9Kw5unsdvCKF-GCvdg0-zJQ,15298
|
|
|
167
167
|
gitea/issue/issue.py,sha256=wpXoGH9JQps3ypbGyzOkqKacGGnDxXxwFWrPEtORXfw,28894
|
|
168
168
|
gitea/issue/project_column.py,sha256=7TpnQcq9-hqc9eEciluplHQmSLiEBkRBr-lH7y6L9s8,15005
|
|
169
169
|
gitea/label/__init__.py,sha256=cHdpsWsPWOX-q6k1JRzPOFSRafJUj907lmndLntjZjQ,690
|
|
170
|
-
gitea/label/async_label.py,sha256=
|
|
171
|
-
gitea/label/base.py,sha256=
|
|
172
|
-
gitea/label/label.py,sha256=
|
|
170
|
+
gitea/label/async_label.py,sha256=11UPD9T15Z3_Z2f63a9oxnwy__qDXTrIECZ03S0ecgE,10049
|
|
171
|
+
gitea/label/base.py,sha256=8zD3r7n6iC4d4bnnDgTB8yfuu0GuZQFoNTC6T7xUcQ4,6483
|
|
172
|
+
gitea/label/label.py,sha256=sIA4f08R1jN30jIMH3h92nq7953rdh2da3TeHJQB2_o,9781
|
|
173
173
|
gitea/milestone/__init__.py,sha256=N5IclwSWdsJXJLK0vDtVIajk-TWstEsfom5FMN4AB50,750
|
|
174
174
|
gitea/milestone/async_milestone.py,sha256=2ofOOtfYp3nyOxEu021mEUsbsclflCr8C4oZVjAj2Ck,5197
|
|
175
175
|
gitea/milestone/base.py,sha256=ZWCP6UungakNkfdezfbMpDDgXr_5Fu7K5ceXgNF3s0g,3437
|
|
@@ -210,8 +210,8 @@ gitea/utils/response.py,sha256=dDk-joN6TA3blefFGTxWTs41dHOUapQs3OxoOqiMg6o,5945
|
|
|
210
210
|
gitea/watch/__init__.py,sha256=xeLLpoldkTAJda4WPOVa6NvE1i_X2VcUcgiMFoK9ImU,1422
|
|
211
211
|
gitea/watch/changes.py,sha256=lSeq3-V8IjvcoZFp5teciiezOlQa89vhaWju80JEHO4,14418
|
|
212
212
|
gitea/watch/state.py,sha256=ZmwPnTRavdyIJuXIY0QllVCh56O_p9oPdVzS-pt0oCs,17856
|
|
213
|
-
python_gitea-0.12.
|
|
214
|
-
python_gitea-0.12.
|
|
215
|
-
python_gitea-0.12.
|
|
216
|
-
python_gitea-0.12.
|
|
217
|
-
python_gitea-0.12.
|
|
213
|
+
python_gitea-0.12.2.dist-info/METADATA,sha256=PgXEdpd3tfl6AKhOdc6MqZ-XrpRqAkb0wluhPpmf9Go,7634
|
|
214
|
+
python_gitea-0.12.2.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
215
|
+
python_gitea-0.12.2.dist-info/entry_points.txt,sha256=m5yt-1RMRZRFMj_M-Un5E4eOkM113S9EppAHp0aCzEY,49
|
|
216
|
+
python_gitea-0.12.2.dist-info/licenses/LICENSE,sha256=4dW9zcCxiQKAu4CyNKJTRmvzdJCCHDxbPGuQ-YALpsY,1137
|
|
217
|
+
python_gitea-0.12.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|