python-gitlab 5.6.0__py3-none-any.whl → 6.0.0__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.
- gitlab/__init__.py +0 -1
- gitlab/_backends/protocol.py +9 -13
- gitlab/_backends/requests_backend.py +12 -12
- gitlab/_version.py +1 -1
- gitlab/base.py +48 -48
- gitlab/cli.py +14 -24
- gitlab/client.py +114 -140
- gitlab/config.py +16 -17
- gitlab/exceptions.py +7 -5
- gitlab/mixins.py +154 -238
- gitlab/types.py +13 -14
- gitlab/utils.py +32 -43
- gitlab/v4/cli.py +50 -53
- gitlab/v4/objects/__init__.py +1 -0
- gitlab/v4/objects/access_requests.py +11 -3
- gitlab/v4/objects/appearance.py +12 -14
- gitlab/v4/objects/applications.py +5 -6
- gitlab/v4/objects/artifacts.py +10 -17
- gitlab/v4/objects/audit_events.py +4 -19
- gitlab/v4/objects/award_emojis.py +13 -57
- gitlab/v4/objects/badges.py +4 -19
- gitlab/v4/objects/boards.py +7 -27
- gitlab/v4/objects/branches.py +3 -15
- gitlab/v4/objects/broadcast_messages.py +3 -13
- gitlab/v4/objects/bulk_imports.py +6 -14
- gitlab/v4/objects/ci_lint.py +7 -13
- gitlab/v4/objects/cluster_agents.py +3 -13
- gitlab/v4/objects/clusters.py +13 -23
- gitlab/v4/objects/commits.py +23 -28
- gitlab/v4/objects/container_registry.py +13 -19
- gitlab/v4/objects/custom_attributes.py +16 -21
- gitlab/v4/objects/deploy_keys.py +22 -19
- gitlab/v4/objects/deploy_tokens.py +14 -32
- gitlab/v4/objects/deployments.py +13 -15
- gitlab/v4/objects/discussions.py +13 -29
- gitlab/v4/objects/draft_notes.py +4 -14
- gitlab/v4/objects/environments.py +13 -21
- gitlab/v4/objects/epics.py +14 -17
- gitlab/v4/objects/events.py +27 -79
- gitlab/v4/objects/export_import.py +7 -19
- gitlab/v4/objects/features.py +11 -12
- gitlab/v4/objects/files.py +23 -38
- gitlab/v4/objects/geo_nodes.py +7 -11
- gitlab/v4/objects/group_access_tokens.py +6 -13
- gitlab/v4/objects/groups.py +40 -37
- gitlab/v4/objects/hooks.py +4 -17
- gitlab/v4/objects/integrations.py +7 -18
- gitlab/v4/objects/invitations.py +12 -23
- gitlab/v4/objects/issues.py +21 -27
- gitlab/v4/objects/iterations.py +4 -8
- gitlab/v4/objects/job_token_scope.py +18 -14
- gitlab/v4/objects/jobs.py +17 -32
- gitlab/v4/objects/keys.py +8 -11
- gitlab/v4/objects/labels.py +19 -30
- gitlab/v4/objects/ldap.py +25 -9
- gitlab/v4/objects/member_roles.py +102 -0
- gitlab/v4/objects/members.py +11 -29
- gitlab/v4/objects/merge_request_approvals.py +31 -44
- gitlab/v4/objects/merge_requests.py +30 -40
- gitlab/v4/objects/merge_trains.py +3 -6
- gitlab/v4/objects/milestones.py +23 -29
- gitlab/v4/objects/namespaces.py +4 -10
- gitlab/v4/objects/notes.py +26 -69
- gitlab/v4/objects/notification_settings.py +5 -14
- gitlab/v4/objects/package_protection_rules.py +8 -8
- gitlab/v4/objects/packages.py +22 -37
- gitlab/v4/objects/pages.py +8 -14
- gitlab/v4/objects/personal_access_tokens.py +7 -10
- gitlab/v4/objects/pipelines.py +38 -47
- gitlab/v4/objects/project_access_tokens.py +6 -13
- gitlab/v4/objects/projects.py +54 -76
- gitlab/v4/objects/push_rules.py +13 -15
- gitlab/v4/objects/registry_protection_repository_rules.py +6 -7
- gitlab/v4/objects/registry_protection_rules.py +7 -11
- gitlab/v4/objects/releases.py +6 -20
- gitlab/v4/objects/repositories.py +25 -34
- gitlab/v4/objects/resource_groups.py +10 -15
- gitlab/v4/objects/reviewers.py +4 -2
- gitlab/v4/objects/runners.py +14 -13
- gitlab/v4/objects/secure_files.py +8 -21
- gitlab/v4/objects/service_accounts.py +7 -5
- gitlab/v4/objects/settings.py +13 -14
- gitlab/v4/objects/sidekiq.py +17 -18
- gitlab/v4/objects/snippets.py +78 -66
- gitlab/v4/objects/statistics.py +8 -23
- gitlab/v4/objects/status_checks.py +6 -3
- gitlab/v4/objects/tags.py +3 -13
- gitlab/v4/objects/templates.py +11 -59
- gitlab/v4/objects/todos.py +3 -6
- gitlab/v4/objects/topics.py +10 -21
- gitlab/v4/objects/triggers.py +3 -13
- gitlab/v4/objects/users.py +87 -93
- gitlab/v4/objects/variables.py +4 -19
- gitlab/v4/objects/wikis.py +4 -19
- {python_gitlab-5.6.0.dist-info → python_gitlab-6.0.0.dist-info}/METADATA +3 -2
- python_gitlab-6.0.0.dist-info/RECORD +107 -0
- {python_gitlab-5.6.0.dist-info → python_gitlab-6.0.0.dist-info}/WHEEL +1 -1
- python_gitlab-5.6.0.dist-info/RECORD +0 -106
- {python_gitlab-5.6.0.dist-info → python_gitlab-6.0.0.dist-info}/entry_points.txt +0 -0
- {python_gitlab-5.6.0.dist-info → python_gitlab-6.0.0.dist-info/licenses}/AUTHORS +0 -0
- {python_gitlab-5.6.0.dist-info → python_gitlab-6.0.0.dist-info/licenses}/COPYING +0 -0
- {python_gitlab-5.6.0.dist-info → python_gitlab-6.0.0.dist-info}/top_level.txt +0 -0
gitlab/__init__.py
CHANGED
gitlab/_backends/protocol.py
CHANGED
@@ -1,15 +1,11 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import abc
|
2
|
-
import
|
3
|
-
from typing import Any, Dict, Optional, Union
|
4
|
+
from typing import Any, Protocol
|
4
5
|
|
5
6
|
import requests
|
6
7
|
from requests_toolbelt.multipart.encoder import MultipartEncoder # type: ignore
|
7
8
|
|
8
|
-
if sys.version_info >= (3, 8):
|
9
|
-
from typing import Protocol
|
10
|
-
else:
|
11
|
-
from typing_extensions import Protocol
|
12
|
-
|
13
9
|
|
14
10
|
class BackendResponse(Protocol):
|
15
11
|
@abc.abstractmethod
|
@@ -22,11 +18,11 @@ class Backend(Protocol):
|
|
22
18
|
self,
|
23
19
|
method: str,
|
24
20
|
url: str,
|
25
|
-
json:
|
26
|
-
data:
|
27
|
-
params:
|
28
|
-
timeout:
|
29
|
-
verify:
|
30
|
-
stream:
|
21
|
+
json: dict[str, Any] | bytes | None,
|
22
|
+
data: dict[str, Any] | MultipartEncoder | None,
|
23
|
+
params: Any | None,
|
24
|
+
timeout: float | None,
|
25
|
+
verify: bool | str | None,
|
26
|
+
stream: bool | None,
|
31
27
|
**kwargs: Any,
|
32
28
|
) -> BackendResponse: ...
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import dataclasses
|
4
|
-
from typing import Any, BinaryIO,
|
4
|
+
from typing import Any, BinaryIO, TYPE_CHECKING
|
5
5
|
|
6
6
|
import requests
|
7
7
|
from requests import PreparedRequest
|
@@ -44,8 +44,8 @@ class JobTokenAuth(TokenAuth, AuthBase):
|
|
44
44
|
@dataclasses.dataclass
|
45
45
|
class SendData:
|
46
46
|
content_type: str
|
47
|
-
data:
|
48
|
-
json:
|
47
|
+
data: dict[str, Any] | MultipartEncoder | None = None
|
48
|
+
json: dict[str, Any] | bytes | None = None
|
49
49
|
|
50
50
|
def __post_init__(self) -> None:
|
51
51
|
if self.json is not None and self.data is not None:
|
@@ -84,7 +84,7 @@ class RequestsResponse(protocol.BackendResponse):
|
|
84
84
|
|
85
85
|
|
86
86
|
class RequestsBackend(protocol.Backend):
|
87
|
-
def __init__(self, session:
|
87
|
+
def __init__(self, session: requests.Session | None = None) -> None:
|
88
88
|
self._client: requests.Session = session or requests.Session()
|
89
89
|
|
90
90
|
@property
|
@@ -93,8 +93,8 @@ class RequestsBackend(protocol.Backend):
|
|
93
93
|
|
94
94
|
@staticmethod
|
95
95
|
def prepare_send_data(
|
96
|
-
files:
|
97
|
-
post_data:
|
96
|
+
files: dict[str, Any] | None = None,
|
97
|
+
post_data: dict[str, Any] | bytes | BinaryIO | None = None,
|
98
98
|
raw: bool = False,
|
99
99
|
) -> SendData:
|
100
100
|
if files:
|
@@ -130,12 +130,12 @@ class RequestsBackend(protocol.Backend):
|
|
130
130
|
self,
|
131
131
|
method: str,
|
132
132
|
url: str,
|
133
|
-
json:
|
134
|
-
data:
|
135
|
-
params:
|
136
|
-
timeout:
|
137
|
-
verify:
|
138
|
-
stream:
|
133
|
+
json: dict[str, Any] | bytes | None = None,
|
134
|
+
data: dict[str, Any] | MultipartEncoder | None = None,
|
135
|
+
params: Any | None = None,
|
136
|
+
timeout: float | None = None,
|
137
|
+
verify: bool | str | None = True,
|
138
|
+
stream: bool | None = False,
|
139
139
|
**kwargs: Any,
|
140
140
|
) -> RequestsResponse:
|
141
141
|
"""Make HTTP request
|
gitlab/_version.py
CHANGED
gitlab/base.py
CHANGED
@@ -1,10 +1,13 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import copy
|
2
4
|
import importlib
|
3
5
|
import json
|
4
6
|
import pprint
|
5
7
|
import textwrap
|
8
|
+
from collections.abc import Iterable
|
6
9
|
from types import ModuleType
|
7
|
-
from typing import Any,
|
10
|
+
from typing import Any, ClassVar, Generic, TYPE_CHECKING, TypeVar
|
8
11
|
|
9
12
|
import gitlab
|
10
13
|
from gitlab import types as g_types
|
@@ -12,11 +15,7 @@ from gitlab.exceptions import GitlabParsingError
|
|
12
15
|
|
13
16
|
from .client import Gitlab, GitlabList
|
14
17
|
|
15
|
-
__all__ = [
|
16
|
-
"RESTObject",
|
17
|
-
"RESTObjectList",
|
18
|
-
"RESTManager",
|
19
|
-
]
|
18
|
+
__all__ = ["RESTObject", "RESTObjectList", "RESTManager"]
|
20
19
|
|
21
20
|
|
22
21
|
_URL_ATTRIBUTE_ERROR = (
|
@@ -40,20 +39,20 @@ class RESTObject:
|
|
40
39
|
object's ``__repr__()`` method.
|
41
40
|
"""
|
42
41
|
|
43
|
-
_id_attr:
|
44
|
-
_attrs:
|
42
|
+
_id_attr: str | None = "id"
|
43
|
+
_attrs: dict[str, Any]
|
45
44
|
_created_from_list: bool # Indicates if object was created from a list() action
|
46
45
|
_module: ModuleType
|
47
|
-
_parent_attrs:
|
48
|
-
_repr_attr:
|
49
|
-
_updated_attrs:
|
46
|
+
_parent_attrs: dict[str, Any]
|
47
|
+
_repr_attr: str | None = None
|
48
|
+
_updated_attrs: dict[str, Any]
|
50
49
|
_lazy: bool
|
51
|
-
manager:
|
50
|
+
manager: RESTManager[Any]
|
52
51
|
|
53
52
|
def __init__(
|
54
53
|
self,
|
55
|
-
manager:
|
56
|
-
attrs:
|
54
|
+
manager: RESTManager[Any],
|
55
|
+
attrs: dict[str, Any],
|
57
56
|
*,
|
58
57
|
created_from_list: bool = False,
|
59
58
|
lazy: bool = False,
|
@@ -77,13 +76,13 @@ class RESTObject:
|
|
77
76
|
self.__dict__["_parent_attrs"] = self.manager.parent_attrs
|
78
77
|
self._create_managers()
|
79
78
|
|
80
|
-
def __getstate__(self) ->
|
79
|
+
def __getstate__(self) -> dict[str, Any]:
|
81
80
|
state = self.__dict__.copy()
|
82
81
|
module = state.pop("_module")
|
83
82
|
state["_module_name"] = module.__name__
|
84
83
|
return state
|
85
84
|
|
86
|
-
def __setstate__(self, state:
|
85
|
+
def __setstate__(self, state: dict[str, Any]) -> None:
|
87
86
|
module_name = state.pop("_module_name")
|
88
87
|
self.__dict__.update(state)
|
89
88
|
self.__dict__["_module"] = importlib.import_module(module_name)
|
@@ -136,7 +135,7 @@ class RESTObject:
|
|
136
135
|
def __setattr__(self, name: str, value: Any) -> None:
|
137
136
|
self.__dict__["_updated_attrs"][name] = value
|
138
137
|
|
139
|
-
def asdict(self, *, with_parent_attrs: bool = False) ->
|
138
|
+
def asdict(self, *, with_parent_attrs: bool = False) -> dict[str, Any]:
|
140
139
|
data = {}
|
141
140
|
if with_parent_attrs:
|
142
141
|
data.update(copy.deepcopy(self._parent_attrs))
|
@@ -145,7 +144,7 @@ class RESTObject:
|
|
145
144
|
return data
|
146
145
|
|
147
146
|
@property
|
148
|
-
def attributes(self) ->
|
147
|
+
def attributes(self) -> dict[str, Any]:
|
149
148
|
return self.asdict(with_parent_attrs=True)
|
150
149
|
|
151
150
|
def to_json(self, *, with_parent_attrs: bool = False, **kwargs: Any) -> str:
|
@@ -220,11 +219,11 @@ class RESTObject:
|
|
220
219
|
# Since we have our own __setattr__ method, we can't use setattr()
|
221
220
|
self.__dict__[attr] = manager
|
222
221
|
|
223
|
-
def _update_attrs(self, new_attrs:
|
222
|
+
def _update_attrs(self, new_attrs: dict[str, Any]) -> None:
|
224
223
|
self.__dict__["_updated_attrs"] = {}
|
225
224
|
self.__dict__["_attrs"] = new_attrs
|
226
225
|
|
227
|
-
def get_id(self) ->
|
226
|
+
def get_id(self) -> int | str | None:
|
228
227
|
"""Returns the id of the resource."""
|
229
228
|
if self._id_attr is None or not hasattr(self, self._id_attr):
|
230
229
|
return None
|
@@ -234,7 +233,7 @@ class RESTObject:
|
|
234
233
|
return id_val
|
235
234
|
|
236
235
|
@property
|
237
|
-
def _repr_value(self) ->
|
236
|
+
def _repr_value(self) -> str | None:
|
238
237
|
"""Safely returns the human-readable resource name if present."""
|
239
238
|
if self._repr_attr is None or not hasattr(self, self._repr_attr):
|
240
239
|
return None
|
@@ -244,7 +243,7 @@ class RESTObject:
|
|
244
243
|
return repr_val
|
245
244
|
|
246
245
|
@property
|
247
|
-
def encoded_id(self) ->
|
246
|
+
def encoded_id(self) -> int | str | None:
|
248
247
|
"""Ensure that the ID is url-encoded so that it can be safely used in a URL
|
249
248
|
path"""
|
250
249
|
obj_id = self.get_id()
|
@@ -253,7 +252,10 @@ class RESTObject:
|
|
253
252
|
return obj_id
|
254
253
|
|
255
254
|
|
256
|
-
|
255
|
+
TObjCls = TypeVar("TObjCls", bound=RESTObject)
|
256
|
+
|
257
|
+
|
258
|
+
class RESTObjectList(Generic[TObjCls]):
|
257
259
|
"""Generator object representing a list of RESTObject's.
|
258
260
|
|
259
261
|
This generator uses the Gitlab pagination system to fetch new data when
|
@@ -269,7 +271,7 @@ class RESTObjectList:
|
|
269
271
|
"""
|
270
272
|
|
271
273
|
def __init__(
|
272
|
-
self, manager:
|
274
|
+
self, manager: RESTManager[TObjCls], obj_cls: type[TObjCls], _list: GitlabList
|
273
275
|
) -> None:
|
274
276
|
"""Creates an objects list from a GitlabList.
|
275
277
|
|
@@ -285,16 +287,16 @@ class RESTObjectList:
|
|
285
287
|
self._obj_cls = obj_cls
|
286
288
|
self._list = _list
|
287
289
|
|
288
|
-
def __iter__(self) ->
|
290
|
+
def __iter__(self) -> RESTObjectList[TObjCls]:
|
289
291
|
return self
|
290
292
|
|
291
293
|
def __len__(self) -> int:
|
292
294
|
return len(self._list)
|
293
295
|
|
294
|
-
def __next__(self) ->
|
296
|
+
def __next__(self) -> TObjCls:
|
295
297
|
return self.next()
|
296
298
|
|
297
|
-
def next(self) ->
|
299
|
+
def next(self) -> TObjCls:
|
298
300
|
data = self._list.next()
|
299
301
|
return self._obj_cls(self.manager, data, created_from_list=True)
|
300
302
|
|
@@ -304,7 +306,7 @@ class RESTObjectList:
|
|
304
306
|
return self._list.current_page
|
305
307
|
|
306
308
|
@property
|
307
|
-
def prev_page(self) ->
|
309
|
+
def prev_page(self) -> int | None:
|
308
310
|
"""The previous page number.
|
309
311
|
|
310
312
|
If None, the current page is the first.
|
@@ -312,7 +314,7 @@ class RESTObjectList:
|
|
312
314
|
return self._list.prev_page
|
313
315
|
|
314
316
|
@property
|
315
|
-
def next_page(self) ->
|
317
|
+
def next_page(self) -> int | None:
|
316
318
|
"""The next page number.
|
317
319
|
|
318
320
|
If None, the current page is the last.
|
@@ -320,22 +322,22 @@ class RESTObjectList:
|
|
320
322
|
return self._list.next_page
|
321
323
|
|
322
324
|
@property
|
323
|
-
def per_page(self) ->
|
325
|
+
def per_page(self) -> int | None:
|
324
326
|
"""The number of items per page."""
|
325
327
|
return self._list.per_page
|
326
328
|
|
327
329
|
@property
|
328
|
-
def total_pages(self) ->
|
330
|
+
def total_pages(self) -> int | None:
|
329
331
|
"""The total number of pages."""
|
330
332
|
return self._list.total_pages
|
331
333
|
|
332
334
|
@property
|
333
|
-
def total(self) ->
|
335
|
+
def total(self) -> int | None:
|
334
336
|
"""The total number of items."""
|
335
337
|
return self._list.total
|
336
338
|
|
337
339
|
|
338
|
-
class RESTManager:
|
340
|
+
class RESTManager(Generic[TObjCls]):
|
339
341
|
"""Base class for CRUD operations on objects.
|
340
342
|
|
341
343
|
Derived class must define ``_path`` and ``_obj_cls``.
|
@@ -346,17 +348,17 @@ class RESTManager:
|
|
346
348
|
|
347
349
|
_create_attrs: g_types.RequiredOptional = g_types.RequiredOptional()
|
348
350
|
_update_attrs: g_types.RequiredOptional = g_types.RequiredOptional()
|
349
|
-
_path:
|
350
|
-
_obj_cls:
|
351
|
-
_from_parent_attrs:
|
352
|
-
_types:
|
353
|
-
|
354
|
-
_computed_path:
|
355
|
-
_parent:
|
356
|
-
_parent_attrs:
|
351
|
+
_path: ClassVar[str]
|
352
|
+
_obj_cls: type[TObjCls]
|
353
|
+
_from_parent_attrs: dict[str, Any] = {}
|
354
|
+
_types: dict[str, type[g_types.GitlabAttribute]] = {}
|
355
|
+
|
356
|
+
_computed_path: str
|
357
|
+
_parent: RESTObject | None
|
358
|
+
_parent_attrs: dict[str, Any]
|
357
359
|
gitlab: Gitlab
|
358
360
|
|
359
|
-
def __init__(self, gl: Gitlab, parent:
|
361
|
+
def __init__(self, gl: Gitlab, parent: RESTObject | None = None) -> None:
|
360
362
|
"""REST manager constructor.
|
361
363
|
|
362
364
|
Args:
|
@@ -368,19 +370,17 @@ class RESTManager:
|
|
368
370
|
self._computed_path = self._compute_path()
|
369
371
|
|
370
372
|
@property
|
371
|
-
def parent_attrs(self) ->
|
373
|
+
def parent_attrs(self) -> dict[str, Any] | None:
|
372
374
|
return self._parent_attrs
|
373
375
|
|
374
|
-
def _compute_path(self, path:
|
376
|
+
def _compute_path(self, path: str | None = None) -> str:
|
375
377
|
self._parent_attrs = {}
|
376
378
|
if path is None:
|
377
379
|
path = self._path
|
378
|
-
if path is None:
|
379
|
-
return None
|
380
380
|
if self._parent is None or not self._from_parent_attrs:
|
381
381
|
return path
|
382
382
|
|
383
|
-
data:
|
383
|
+
data: dict[str, gitlab.utils.EncodedId | None] = {}
|
384
384
|
for self_attr, parent_attr in self._from_parent_attrs.items():
|
385
385
|
if not hasattr(self._parent, parent_attr):
|
386
386
|
data[self_attr] = None
|
@@ -390,5 +390,5 @@ class RESTManager:
|
|
390
390
|
return path.format(**data)
|
391
391
|
|
392
392
|
@property
|
393
|
-
def path(self) ->
|
393
|
+
def path(self) -> str:
|
394
394
|
return self._computed_path
|
gitlab/cli.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import argparse
|
2
4
|
import dataclasses
|
3
5
|
import functools
|
@@ -6,19 +8,7 @@ import pathlib
|
|
6
8
|
import re
|
7
9
|
import sys
|
8
10
|
from types import ModuleType
|
9
|
-
from typing import
|
10
|
-
Any,
|
11
|
-
Callable,
|
12
|
-
cast,
|
13
|
-
Dict,
|
14
|
-
NoReturn,
|
15
|
-
Optional,
|
16
|
-
Tuple,
|
17
|
-
Type,
|
18
|
-
TYPE_CHECKING,
|
19
|
-
TypeVar,
|
20
|
-
Union,
|
21
|
-
)
|
11
|
+
from typing import Any, Callable, cast, NoReturn, TYPE_CHECKING, TypeVar
|
22
12
|
|
23
13
|
from requests.structures import CaseInsensitiveDict
|
24
14
|
|
@@ -33,11 +23,11 @@ camel_lowerupper_regex = re.compile(r"([a-z\d])([A-Z])")
|
|
33
23
|
|
34
24
|
@dataclasses.dataclass
|
35
25
|
class CustomAction:
|
36
|
-
required:
|
37
|
-
optional:
|
26
|
+
required: tuple[str, ...]
|
27
|
+
optional: tuple[str, ...]
|
38
28
|
in_object: bool
|
39
29
|
requires_id: bool # if the `_id_attr` value should be a required argument
|
40
|
-
help:
|
30
|
+
help: str | None # help text for the custom action
|
41
31
|
|
42
32
|
|
43
33
|
# custom_actions = {
|
@@ -45,7 +35,7 @@ class CustomAction:
|
|
45
35
|
# action: CustomAction,
|
46
36
|
# },
|
47
37
|
# }
|
48
|
-
custom_actions:
|
38
|
+
custom_actions: dict[str, dict[str, CustomAction]] = {}
|
49
39
|
|
50
40
|
|
51
41
|
# For an explanation of how these type-hints work see:
|
@@ -57,12 +47,12 @@ __F = TypeVar("__F", bound=Callable[..., Any])
|
|
57
47
|
|
58
48
|
def register_custom_action(
|
59
49
|
*,
|
60
|
-
cls_names:
|
61
|
-
required:
|
62
|
-
optional:
|
63
|
-
custom_action:
|
50
|
+
cls_names: str | tuple[str, ...],
|
51
|
+
required: tuple[str, ...] = (),
|
52
|
+
optional: tuple[str, ...] = (),
|
53
|
+
custom_action: str | None = None,
|
64
54
|
requires_id: bool = True, # if the `_id_attr` value should be a required argument
|
65
|
-
help:
|
55
|
+
help: str | None = None, # help text for the action
|
66
56
|
) -> Callable[[__F], __F]:
|
67
57
|
def wrap(f: __F) -> __F:
|
68
58
|
@functools.wraps(f)
|
@@ -98,7 +88,7 @@ def register_custom_action(
|
|
98
88
|
return wrap
|
99
89
|
|
100
90
|
|
101
|
-
def die(msg: str, e:
|
91
|
+
def die(msg: str, e: Exception | None = None) -> NoReturn:
|
102
92
|
if e:
|
103
93
|
msg = f"{msg} ({e})"
|
104
94
|
sys.stderr.write(f"{msg}\n")
|
@@ -107,7 +97,7 @@ def die(msg: str, e: Optional[Exception] = None) -> NoReturn:
|
|
107
97
|
|
108
98
|
def gitlab_resource_to_cls(
|
109
99
|
gitlab_resource: str, namespace: ModuleType
|
110
|
-
) ->
|
100
|
+
) -> type[RESTObject]:
|
111
101
|
classes = CaseInsensitiveDict(namespace.__dict__)
|
112
102
|
lowercase_class = gitlab_resource.replace("-", "")
|
113
103
|
class_type = classes[lowercase_class]
|