| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -20,11 +20,12 @@ | |||
| 20 | 20 | import textwrap | |
| 21 | 21 | from dataclasses import dataclass | |
| 22 | 22 | from types import ModuleType | |
| 23 | - from typing import Any, Dict, Iterable, Optional, Tuple, Type, Union | ||
| 23 | + from typing import Any, Callable, Dict, Iterable, Optional, Tuple, Type, Union | ||
| 24 | 24 | ||
| 25 | 25 | import gitlab | |
| 26 | 26 | from gitlab import types as g_types | |
| 27 | 27 | from gitlab.exceptions import GitlabParsingError | |
| 28 | + from gitlab.types import F | ||
| 28 | 29 | ||
| 29 | 30 | from .client import Gitlab, GitlabList | |
| 30 | 31 | ||
@@ -336,6 +337,21 @@ class RequiredOptional: | |||
| 336 | 337 | optional: Tuple[str, ...] = () | |
| 337 | 338 | ||
| 338 | 339 | ||
| 340 | + def custom_attrs(required: tuple = (), optional: tuple = ()) -> Callable[[F], F]: | ||
| 341 | + """Decorates a custom method to add a RequiredOptional attribute. | ||
| 342 | + | ||
| 343 | + Args: | ||
| 344 | + required: A tuple of API attributes required in the custom method | ||
| 345 | + optional: A tuple of API attributes optional in the custom method | ||
| 346 | + """ | ||
| 347 | + | ||
| 348 | + def decorator(func: F) -> F: | ||
| 349 | + setattr(func, "_custom_attrs", RequiredOptional(required, optional)) | ||
| 350 | + return func | ||
| 351 | + | ||
| 352 | + return decorator | ||
| 353 | + | ||
| 354 | + | ||
| 339 | 355 | class RESTManager: | |
| 340 | 356 | """Base class for CRUD operations on objects. | |
| 341 | 357 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,12 +23,13 @@ | |||
| 23 | 23 | import re | |
| 24 | 24 | import sys | |
| 25 | 25 | from types import ModuleType | |
| 26 | - from typing import Any, Callable, cast, Dict, Optional, Tuple, Type, TypeVar, Union | ||
| 26 | + from typing import Any, Callable, cast, Dict, Optional, Tuple, Type, Union | ||
| 27 | 27 | ||
| 28 | 28 | from requests.structures import CaseInsensitiveDict | |
| 29 | 29 | ||
| 30 | 30 | import gitlab.config | |
| 31 | - from gitlab.base import RESTObject | ||
| 31 | + from gitlab.base import RequiredOptional, RESTObject | ||
| 32 | + from gitlab.types import F | ||
| 32 | 33 | ||
| 33 | 34 | # This regex is based on: | |
| 34 | 35 | # https://github.com/jpvanhal/inflection/blob/master/inflection/__init__.py | |
@@ -43,24 +44,18 @@ | |||
| 43 | 44 | custom_actions: Dict[str, Dict[str, Tuple[Tuple[str, ...], Tuple[str, ...], bool]]] = {} | |
| 44 | 45 | ||
| 45 | 46 | ||
| 46 | - # For an explanation of how these type-hints work see: | ||
| 47 | - # https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators | ||
| 48 | - # | ||
| 49 | - # The goal here is that functions which get decorated will retain their types. | ||
| 50 | - __F = TypeVar("__F", bound=Callable[..., Any]) | ||
| 51 | - | ||
| 52 | - | ||
| 53 | 47 | def register_custom_action( | |
| 54 | 48 | cls_names: Union[str, Tuple[str, ...]], | |
| 55 | - mandatory: Tuple[str, ...] = (), | ||
| 56 | - optional: Tuple[str, ...] = (), | ||
| 57 | 49 | custom_action: Optional[str] = None, | |
| 58 | - ) -> Callable[[__F], __F]: | ||
| 59 | - def wrap(f: __F) -> __F: | ||
| 50 | + ) -> Callable[[F], F]: | ||
| 51 | + def wrap(f: F) -> F: | ||
| 60 | 52 | @functools.wraps(f) | |
| 61 | 53 | def wrapped_f(*args: Any, **kwargs: Any) -> Any: | |
| 62 | 54 | return f(*args, **kwargs) | |
| 63 | 55 | ||
| 56 | + action = custom_action or f.__name__.replace("_", "-") | ||
| 57 | + custom_attrs = getattr(f, "_custom_attrs", RequiredOptional()) | ||
| 58 | + | ||
| 64 | 59 | # in_obj defines whether the method belongs to the obj or the manager | |
| 65 | 60 | in_obj = True | |
| 66 | 61 | if isinstance(cls_names, tuple): | |
@@ -76,10 +71,13 @@ def wrapped_f(*args: Any, **kwargs: Any) -> Any: | |||
| 76 | 71 | if final_name not in custom_actions: | |
| 77 | 72 | custom_actions[final_name] = {} | |
| 78 | 73 | ||
| 79 | - action = custom_action or f.__name__.replace("_", "-") | ||
| 80 | - custom_actions[final_name][action] = (mandatory, optional, in_obj) | ||
| 74 | + custom_actions[final_name][action] = ( | ||
| 75 | + custom_attrs.required, | ||
| 76 | + custom_attrs.optional, | ||
| 77 | + in_obj, | ||
| 78 | + ) | ||
| 81 | 79 | ||
| 82 | - return cast(__F, wrapped_f) | ||
| 80 | + return cast(F, wrapped_f) | ||
| 83 | 81 | ||
| 84 | 82 | return wrap | |
| 85 | 83 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -16,7 +16,9 @@ | |||
| 16 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 17 | 17 | ||
| 18 | 18 | import functools | |
| 19 | - from typing import Any, Callable, cast, Optional, Type, TYPE_CHECKING, TypeVar, Union | ||
| 19 | + from typing import Any, Callable, cast, Optional, Type, TYPE_CHECKING, Union | ||
| 20 | + | ||
| 21 | + from gitlab.types import F | ||
| 20 | 22 | ||
| 21 | 23 | ||
| 22 | 24 | class GitlabError(Exception): | |
@@ -287,14 +289,7 @@ class GitlabUnfollowError(GitlabOperationError): | |||
| 287 | 289 | pass | |
| 288 | 290 | ||
| 289 | 291 | ||
| 290 | - # For an explanation of how these type-hints work see: | ||
| 291 | - # https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators | ||
| 292 | - # | ||
| 293 | - # The goal here is that functions which get decorated will retain their types. | ||
| 294 | - __F = TypeVar("__F", bound=Callable[..., Any]) | ||
| 295 | - | ||
| 296 | - | ||
| 297 | - def on_http_error(error: Type[Exception]) -> Callable[[__F], __F]: | ||
| 292 | + def on_http_error(error: Type[Exception]) -> Callable[[F], F]: | ||
| 298 | 293 | """Manage GitlabHttpError exceptions. | |
| 299 | 294 | ||
| 300 | 295 | This decorator function can be used to catch GitlabHttpError exceptions | |
@@ -304,14 +299,14 @@ def on_http_error(error: Type[Exception]) -> Callable[[__F], __F]: | |||
| 304 | 299 | The exception type to raise -- must inherit from GitlabError | |
| 305 | 300 | """ | |
| 306 | 301 | ||
| 307 | - def wrap(f: __F) -> __F: | ||
| 302 | + def wrap(f: F) -> F: | ||
| 308 | 303 | @functools.wraps(f) | |
| 309 | 304 | def wrapped_f(*args: Any, **kwargs: Any) -> Any: | |
| 310 | 305 | try: | |
| 311 | 306 | return f(*args, **kwargs) | |
| 312 | 307 | except GitlabHttpError as e: | |
| 313 | 308 | raise error(e.error_message, e.response_code, e.response_body) from e | |
| 314 | 309 | ||
| 315 | - return cast(__F, wrapped_f) | ||
| 310 | + return cast(F, wrapped_f) | ||
| 316 | 311 | ||
| 317 | 312 | return wrap | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -578,9 +578,8 @@ class AccessRequestMixin(_RestObjectBase): | |||
| 578 | 578 | _updated_attrs: Dict[str, Any] | |
| 579 | 579 | manager: base.RESTManager | |
| 580 | 580 | ||
| 581 | - @cli.register_custom_action( | ||
| 582 | - ("ProjectAccessRequest", "GroupAccessRequest"), (), ("access_level",) | ||
| 583 | - ) | ||
| 581 | + @cli.register_custom_action(("ProjectAccessRequest", "GroupAccessRequest")) | ||
| 582 | + @base.custom_attrs(optional=("access_level",)) | ||
| 584 | 583 | @exc.on_http_error(exc.GitlabUpdateError) | |
| 585 | 584 | def approve( | |
| 586 | 585 | self, access_level: int = gitlab.const.DEVELOPER_ACCESS, **kwargs: Any | |
@@ -752,7 +751,8 @@ def time_stats(self, **kwargs: Any) -> Dict[str, Any]: | |||
| 752 | 751 | assert not isinstance(result, requests.Response) | |
| 753 | 752 | return result | |
| 754 | 753 | ||
| 755 | - @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"), ("duration",)) | ||
| 754 | + @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest")) | ||
| 755 | + @base.custom_attrs(required=("duration",)) | ||
| 756 | 756 | @exc.on_http_error(exc.GitlabTimeTrackingError) | |
| 757 | 757 | def time_estimate(self, duration: str, **kwargs: Any) -> Dict[str, Any]: | |
| 758 | 758 | """Set an estimated time of work for the object. | |
@@ -790,7 +790,8 @@ def reset_time_estimate(self, **kwargs: Any) -> Dict[str, Any]: | |||
| 790 | 790 | assert not isinstance(result, requests.Response) | |
| 791 | 791 | return result | |
| 792 | 792 | ||
| 793 | - @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest"), ("duration",)) | ||
| 793 | + @cli.register_custom_action(("ProjectIssue", "ProjectMergeRequest")) | ||
| 794 | + @base.custom_attrs(required=("duration",)) | ||
| 794 | 795 | @exc.on_http_error(exc.GitlabTimeTrackingError) | |
| 795 | 796 | def add_spent_time(self, duration: str, **kwargs: Any) -> Dict[str, Any]: | |
| 796 | 797 | """Add time spent working on the object. | |
@@ -866,9 +867,8 @@ def participants(self, **kwargs: Any) -> Dict[str, Any]: | |||
| 866 | 867 | ||
| 867 | 868 | ||
| 868 | 869 | class BadgeRenderMixin(_RestManagerBase): | |
| 869 | - @cli.register_custom_action( | ||
| 870 | - ("GroupBadgeManager", "ProjectBadgeManager"), ("link_url", "image_url") | ||
| 871 | - ) | ||
| 870 | + @cli.register_custom_action(("GroupBadgeManager", "ProjectBadgeManager")) | ||
| 871 | + @base.custom_attrs(required=("link_url", "image_url")) | ||
| 872 | 872 | @exc.on_http_error(exc.GitlabRenderError) | |
| 873 | 873 | def render(self, link_url: str, image_url: str, **kwargs: Any) -> Dict[str, Any]: | |
| 874 | 874 | """Preview link_url and image_url after interpolation. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -15,7 +15,11 @@ | |||
| 15 | 15 | # You should have received a copy of the GNU Lesser General Public License | |
| 16 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 17 | 17 | ||
| 18 | - from typing import Any, Optional, TYPE_CHECKING | ||
| 18 | + from typing import Any, Callable, Optional, TYPE_CHECKING, TypeVar | ||
| 19 | + | ||
| 20 | + # TypeVar for decorators so that decorated functions retain their signatures. | ||
| 21 | + # See https://mypy.readthedocs.io/en/stable/generics.html#declaring-decorators | ||
| 22 | + F = TypeVar("F", bound=Callable[..., Any]) | ||
| 19 | 23 | ||
| 20 | 24 | ||
| 21 | 25 | class GitlabAttribute: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,7 +9,7 @@ | |||
| 9 | 9 | from gitlab import cli | |
| 10 | 10 | from gitlab import exceptions as exc | |
| 11 | 11 | from gitlab import utils | |
| 12 | - from gitlab.base import RESTManager, RESTObject | ||
| 12 | + from gitlab.base import custom_attrs, RESTManager, RESTObject | ||
| 13 | 13 | ||
| 14 | 14 | __all__ = ["ProjectArtifact", "ProjectArtifactManager"] | |
| 15 | 15 | ||
@@ -25,9 +25,8 @@ class ProjectArtifactManager(RESTManager): | |||
| 25 | 25 | _path = "/projects/{project_id}/jobs/artifacts" | |
| 26 | 26 | _from_parent_attrs = {"project_id": "id"} | |
| 27 | 27 | ||
| 28 | - @cli.register_custom_action( | ||
| 29 | - "Project", ("ref_name", "job"), ("job_token",), custom_action="artifacts" | ||
| 30 | - ) | ||
| 28 | + @cli.register_custom_action("Project", custom_action="artifacts") | ||
| 29 | + @custom_attrs(required=("ref_name", "job"), optional=("job_token",)) | ||
| 31 | 30 | def __call__( | |
| 32 | 31 | self, | |
| 33 | 32 | *args: Any, | |
@@ -62,9 +61,8 @@ def delete(self, **kwargs: Any) -> None: | |||
| 62 | 61 | assert path is not None | |
| 63 | 62 | self.gitlab.http_delete(path, **kwargs) | |
| 64 | 63 | ||
| 65 | - @cli.register_custom_action( | ||
| 66 | - "ProjectArtifactManager", ("ref_name", "job"), ("job_token",) | ||
| 67 | - ) | ||
| 64 | + @cli.register_custom_action("ProjectArtifactManager") | ||
| 65 | + @custom_attrs(required=("ref_name", "job"), optional=("job_token",)) | ||
| 68 | 66 | @exc.on_http_error(exc.GitlabGetError) | |
| 69 | 67 | def download( | |
| 70 | 68 | self, | |
@@ -105,9 +103,8 @@ def download( | |||
| 105 | 103 | assert isinstance(result, requests.Response) | |
| 106 | 104 | return utils.response_content(result, streamed, action, chunk_size) | |
| 107 | 105 | ||
| 108 | - @cli.register_custom_action( | ||
| 109 | - "ProjectArtifactManager", ("ref_name", "artifact_path", "job") | ||
| 110 | - ) | ||
| 106 | + @cli.register_custom_action("ProjectArtifactManager") | ||
| 107 | + @custom_attrs(required=("ref_name", "artifact_path", "job")) | ||
| 111 | 108 | @exc.on_http_error(exc.GitlabGetError) | |
| 112 | 109 | def raw( | |
| 113 | 110 | self, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,7 +3,7 @@ | |||
| 3 | 3 | import requests | |
| 4 | 4 | ||
| 5 | 5 | import gitlab | |
| 6 | - from gitlab import cli | ||
| 6 | + from gitlab import base, cli | ||
| 7 | 7 | from gitlab import exceptions as exc | |
| 8 | 8 | from gitlab.base import RequiredOptional, RESTManager, RESTObject | |
| 9 | 9 | from gitlab.mixins import CreateMixin, ListMixin, RefreshMixin, RetrieveMixin | |
@@ -45,7 +45,8 @@ def diff(self, **kwargs: Any) -> Union[gitlab.GitlabList, List[Dict[str, Any]]]: | |||
| 45 | 45 | path = f"{self.manager.path}/{self.encoded_id}/diff" | |
| 46 | 46 | return self.manager.gitlab.http_list(path, **kwargs) | |
| 47 | 47 | ||
| 48 | - @cli.register_custom_action("ProjectCommit", ("branch",)) | ||
| 48 | + @cli.register_custom_action("ProjectCommit") | ||
| 49 | + @base.custom_attrs(required=("branch",)) | ||
| 49 | 50 | @exc.on_http_error(exc.GitlabCherryPickError) | |
| 50 | 51 | def cherry_pick(self, branch: str, **kwargs: Any) -> None: | |
| 51 | 52 | """Cherry-pick a commit into a branch. | |
@@ -62,7 +63,8 @@ def cherry_pick(self, branch: str, **kwargs: Any) -> None: | |||
| 62 | 63 | post_data = {"branch": branch} | |
| 63 | 64 | self.manager.gitlab.http_post(path, post_data=post_data, **kwargs) | |
| 64 | 65 | ||
| 65 | - @cli.register_custom_action("ProjectCommit", optional=("type",)) | ||
| 66 | + @cli.register_custom_action("ProjectCommit") | ||
| 67 | + @base.custom_attrs(optional=("type",)) | ||
| 66 | 68 | @exc.on_http_error(exc.GitlabGetError) | |
| 67 | 69 | def refs( | |
| 68 | 70 | self, type: str = "all", **kwargs: Any | |
@@ -104,7 +106,8 @@ def merge_requests( | |||
| 104 | 106 | path = f"{self.manager.path}/{self.encoded_id}/merge_requests" | |
| 105 | 107 | return self.manager.gitlab.http_list(path, **kwargs) | |
| 106 | 108 | ||
| 107 | - @cli.register_custom_action("ProjectCommit", ("branch",)) | ||
| 109 | + @cli.register_custom_action("ProjectCommit") | ||
| 110 | + @base.custom_attrs(required=("branch",)) | ||
| 108 | 111 | @exc.on_http_error(exc.GitlabRevertError) | |
| 109 | 112 | def revert( | |
| 110 | 113 | self, branch: str, **kwargs: Any | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,6 +1,6 @@ | |||
| 1 | 1 | from typing import Any, cast, TYPE_CHECKING, Union | |
| 2 | 2 | ||
| 3 | - from gitlab import cli | ||
| 3 | + from gitlab import base, cli | ||
| 4 | 4 | from gitlab import exceptions as exc | |
| 5 | 5 | from gitlab.base import RESTManager, RESTObject | |
| 6 | 6 | from gitlab.mixins import DeleteMixin, ListMixin, ObjectDeleteMixin, RetrieveMixin | |
@@ -32,9 +32,9 @@ class ProjectRegistryTagManager(DeleteMixin, RetrieveMixin, RESTManager): | |||
| 32 | 32 | _from_parent_attrs = {"project_id": "project_id", "repository_id": "id"} | |
| 33 | 33 | _path = "/projects/{project_id}/registry/repositories/{repository_id}/tags" | |
| 34 | 34 | ||
| 35 | - @cli.register_custom_action( | ||
| 36 | - "ProjectRegistryTagManager", | ||
| 37 | - ("name_regex_delete",), | ||
| 35 | + @cli.register_custom_action("ProjectRegistryTagManager") | ||
| 36 | + @base.custom_attrs( | ||
| 37 | + required=("name_regex_delete",), | ||
| 38 | 38 | optional=("keep_n", "name_regex_keep", "older_than"), | |
| 39 | 39 | ) | |
| 40 | 40 | @exc.on_http_error(exc.GitlabDeleteError) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | 3 | import requests | |
| 4 | 4 | ||
| 5 | - from gitlab import cli | ||
| 5 | + from gitlab import base, cli | ||
| 6 | 6 | from gitlab import exceptions as exc | |
| 7 | 7 | from gitlab.base import RequiredOptional, RESTManager, RESTObject | |
| 8 | 8 | from gitlab.mixins import CRUDMixin, ListMixin, ObjectDeleteMixin, SaveMixin | |
@@ -35,7 +35,8 @@ class ProjectKeyManager(CRUDMixin, RESTManager): | |||
| 35 | 35 | _create_attrs = RequiredOptional(required=("title", "key"), optional=("can_push",)) | |
| 36 | 36 | _update_attrs = RequiredOptional(optional=("title", "can_push")) | |
| 37 | 37 | ||
| 38 | - @cli.register_custom_action("ProjectKeyManager", ("key_id",)) | ||
| 38 | + @cli.register_custom_action("ProjectKeyManager") | ||
| 39 | + @base.custom_attrs(required=("key_id",)) | ||
| 39 | 40 | @exc.on_http_error(exc.GitlabProjectDeployKeyError) | |
| 40 | 41 | def enable( | |
| 41 | 42 | self, key_id: int, **kwargs: Any | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,7 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | 4 | import requests | |
| 5 | 5 | ||
| 6 | - from gitlab import cli | ||
| 6 | + from gitlab import base, cli | ||
| 7 | 7 | from gitlab import exceptions as exc | |
| 8 | 8 | from gitlab import utils | |
| 9 | 9 | from gitlab.base import RequiredOptional, RESTManager, RESTObject | |
@@ -95,7 +95,8 @@ class ProjectFileManager(GetMixin, CreateMixin, UpdateMixin, DeleteMixin, RESTMa | |||
| 95 | 95 | optional=("encoding", "author_email", "author_name"), | |
| 96 | 96 | ) | |
| 97 | 97 | ||
| 98 | - @cli.register_custom_action("ProjectFileManager", ("file_path", "ref")) | ||
| 98 | + @cli.register_custom_action("ProjectFileManager") | ||
| 99 | + @base.custom_attrs(required=("file_path", "ref")) | ||
| 99 | 100 | # NOTE(jlvillal): Signature doesn't match UpdateMixin.update() so ignore | |
| 100 | 101 | # type error | |
| 101 | 102 | def get( # type: ignore | |
@@ -117,10 +118,10 @@ def get( # type: ignore | |||
| 117 | 118 | """ | |
| 118 | 119 | return cast(ProjectFile, GetMixin.get(self, file_path, ref=ref, **kwargs)) | |
| 119 | 120 | ||
| 120 | - @cli.register_custom_action( | ||
| 121 | - "ProjectFileManager", | ||
| 122 | - ("file_path", "branch", "content", "commit_message"), | ||
| 123 | - ("encoding", "author_email", "author_name"), | ||
| 121 | + @cli.register_custom_action("ProjectFileManager") | ||
| 122 | + @base.custom_attrs( | ||
| 123 | + required=("file_path", "branch", "content", "commit_message"), | ||
| 124 | + optional=("encoding", "author_email", "author_name"), | ||
| 124 | 125 | ) | |
| 125 | 126 | @exc.on_http_error(exc.GitlabCreateError) | |
| 126 | 127 | def create( | |
@@ -184,9 +185,8 @@ def update( # type: ignore | |||
| 184 | 185 | assert isinstance(result, dict) | |
| 185 | 186 | return result | |
| 186 | 187 | ||
| 187 | - @cli.register_custom_action( | ||
| 188 | - "ProjectFileManager", ("file_path", "branch", "commit_message") | ||
| 189 | - ) | ||
| 188 | + @cli.register_custom_action("ProjectFileManager") | ||
| 189 | + @base.custom_attrs(required=("file_path", "branch", "commit_message")) | ||
| 190 | 190 | @exc.on_http_error(exc.GitlabDeleteError) | |
| 191 | 191 | # NOTE(jlvillal): Signature doesn't match DeleteMixin.delete() so ignore | |
| 192 | 192 | # type error | |
@@ -210,7 +210,8 @@ def delete( # type: ignore | |||
| 210 | 210 | data = {"branch": branch, "commit_message": commit_message} | |
| 211 | 211 | self.gitlab.http_delete(path, query_data=data, **kwargs) | |
| 212 | 212 | ||
| 213 | - @cli.register_custom_action("ProjectFileManager", ("file_path", "ref")) | ||
| 213 | + @cli.register_custom_action("ProjectFileManager") | ||
| 214 | + @base.custom_attrs(required=("file_path", "ref")) | ||
| 214 | 215 | @exc.on_http_error(exc.GitlabGetError) | |
| 215 | 216 | def raw( | |
| 216 | 217 | self, | |
@@ -251,7 +252,8 @@ def raw( | |||
| 251 | 252 | assert isinstance(result, requests.Response) | |
| 252 | 253 | return utils.response_content(result, streamed, action, chunk_size) | |
| 253 | 254 | ||
| 254 | - @cli.register_custom_action("ProjectFileManager", ("file_path", "ref")) | ||
| 255 | + @cli.register_custom_action("ProjectFileManager") | ||
| 256 | + @base.custom_attrs(required=("file_path", "ref")) | ||
| 255 | 257 | @exc.on_http_error(exc.GitlabListError) | |
| 256 | 258 | def blame(self, file_path: str, ref: str, **kwargs: Any) -> List[Dict[str, Any]]: | |
| 257 | 259 | """Return the content of a file for a commit. | |
| Back | FazBrowse Home | New Git URL |
0 commit comments