From 0c5a1213ba3bb3ec4ed5874db4588d21969e9e80 Mon Sep 17 00:00:00 2001 From: "John L. Villalovos" Date: Tue, 31 May 2022 17:43:22 -0700 Subject: [PATCH 1/2] chore: remove use of '%' string formatter in `gitlab/utils.py` Replace usage with f-string --- gitlab/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gitlab/utils.py b/gitlab/utils.py index 7c9456962..3cdd9679c 100644 --- a/gitlab/utils.py +++ b/gitlab/utils.py @@ -183,6 +183,6 @@ def _validate_attrs( ) if not exclusives: raise AttributeError( - "Must provide one of these attributes: %(attrs)s" - % {"attrs": ", ".join(attributes.exclusive)} + f"Must provide one of these attributes: " + f"{', '.join(attributes.exclusive)}" ) From 9d629bb97af1e14ce8eb4679092de2393e1e3a05 Mon Sep 17 00:00:00 2001 From: "John L. Villalovos" Date: Tue, 31 May 2022 17:54:59 -0700 Subject: [PATCH 2/2] chore: move `utils._validate_attrs` inside `types.RequiredOptional` Move the `validate_attrs` function to be inside the `RequiredOptional` class. It makes sense for it to be part of the class as it is working on data related to the class. --- gitlab/mixins.py | 6 ++--- gitlab/types.py | 29 ++++++++++++++++++++++++- gitlab/utils.py | 29 +------------------------ gitlab/v4/objects/epics.py | 4 ++-- gitlab/v4/objects/files.py | 4 ++-- gitlab/v4/objects/issues.py | 4 ++-- tests/unit/mixins/test_mixin_methods.py | 9 ++++---- 7 files changed, 41 insertions(+), 44 deletions(-) diff --git a/gitlab/mixins.py b/gitlab/mixins.py index ec6fa1831..8e9f0b38c 100644 --- a/gitlab/mixins.py +++ b/gitlab/mixins.py @@ -278,7 +278,7 @@ def create( if data is None: data = {} - utils._validate_attrs(data=data, attributes=self._create_attrs) + self._create_attrs.validate_attrs(data=data) data, files = utils._transform_types(data, self._types) # Handle specific URL for creation @@ -345,9 +345,7 @@ def update( excludes = [] if self._obj_cls is not None and self._obj_cls._id_attr is not None: excludes = [self._obj_cls._id_attr] - utils._validate_attrs( - data=new_data, attributes=self._update_attrs, excludes=excludes - ) + self._update_attrs.validate_attrs(data=new_data, excludes=excludes) new_data, files = utils._transform_types(new_data, self._types) http_method = self._get_update_method() diff --git a/gitlab/types.py b/gitlab/types.py index 38af2e5ad..e2d988af4 100644 --- a/gitlab/types.py +++ b/gitlab/types.py @@ -16,7 +16,7 @@ # along with this program. If not, see . import dataclasses -from typing import Any, Optional, Tuple, TYPE_CHECKING +from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING @dataclasses.dataclass(frozen=True) @@ -25,6 +25,33 @@ class RequiredOptional: optional: Tuple[str, ...] = () exclusive: Tuple[str, ...] = () + def validate_attrs( + self, + *, + data: Dict[str, Any], + excludes: Optional[List[str]] = None, + ) -> None: + if excludes is None: + excludes = [] + + if self.required: + required = [k for k in self.required if k not in excludes] + missing = [attr for attr in required if attr not in data] + if missing: + raise AttributeError(f"Missing attributes: {', '.join(missing)}") + + if self.exclusive: + exclusives = [attr for attr in data if attr in self.exclusive] + if len(exclusives) > 1: + raise AttributeError( + f"Provide only one of these attributes: {', '.join(exclusives)}" + ) + if not exclusives: + raise AttributeError( + f"Must provide one of these attributes: " + f"{', '.join(self.exclusive)}" + ) + class GitlabAttribute: def __init__(self, value: Any = None) -> None: diff --git a/gitlab/utils.py b/gitlab/utils.py index 3cdd9679c..bab670584 100644 --- a/gitlab/utils.py +++ b/gitlab/utils.py @@ -19,7 +19,7 @@ import traceback import urllib.parse import warnings -from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union +from typing import Any, Callable, Dict, Optional, Tuple, Type, Union import requests @@ -159,30 +159,3 @@ def warn( stacklevel=stacklevel, source=source, ) - - -def _validate_attrs( - data: Dict[str, Any], - attributes: types.RequiredOptional, - excludes: Optional[List[str]] = None, -) -> None: - if excludes is None: - excludes = [] - - if attributes.required: - required = [k for k in attributes.required if k not in excludes] - missing = [attr for attr in required if attr not in data] - if missing: - raise AttributeError(f"Missing attributes: {', '.join(missing)}") - - if attributes.exclusive: - exclusives = [attr for attr in data if attr in attributes.exclusive] - if len(exclusives) > 1: - raise AttributeError( - f"Provide only one of these attributes: {', '.join(exclusives)}" - ) - if not exclusives: - raise AttributeError( - f"Must provide one of these attributes: " - f"{', '.join(attributes.exclusive)}" - ) diff --git a/gitlab/v4/objects/epics.py b/gitlab/v4/objects/epics.py index f9b110bba..c92420cfc 100644 --- a/gitlab/v4/objects/epics.py +++ b/gitlab/v4/objects/epics.py @@ -1,7 +1,7 @@ from typing import Any, cast, Dict, Optional, TYPE_CHECKING, Union from gitlab import exceptions as exc -from gitlab import types, utils +from gitlab import types from gitlab.base import RESTManager, RESTObject from gitlab.mixins import ( CreateMixin, @@ -107,7 +107,7 @@ def create( """ if TYPE_CHECKING: assert data is not None - utils._validate_attrs(data=data, attributes=self._create_attrs) + self._create_attrs.validate_attrs(data=data) path = f"{self.path}/{data.pop('issue_id')}" server_data = self.gitlab.http_post(path, **kwargs) if TYPE_CHECKING: diff --git a/gitlab/v4/objects/files.py b/gitlab/v4/objects/files.py index b80d129ee..894195c57 100644 --- a/gitlab/v4/objects/files.py +++ b/gitlab/v4/objects/files.py @@ -145,7 +145,7 @@ def create( if TYPE_CHECKING: assert data is not None - utils._validate_attrs(data=data, attributes=self._create_attrs) + self._create_attrs.validate_attrs(data=data) new_data = data.copy() file_path = utils.EncodedId(new_data.pop("file_path")) path = f"{self.path}/{file_path}" @@ -179,7 +179,7 @@ def update( # type: ignore file_path = utils.EncodedId(file_path) data["file_path"] = file_path path = f"{self.path}/{file_path}" - utils._validate_attrs(data=data, attributes=self._update_attrs) + self._update_attrs.validate_attrs(data=data) result = self.gitlab.http_put(path, post_data=data, **kwargs) if TYPE_CHECKING: assert isinstance(result, dict) diff --git a/gitlab/v4/objects/issues.py b/gitlab/v4/objects/issues.py index f4da4b1a9..736bb5cdb 100644 --- a/gitlab/v4/objects/issues.py +++ b/gitlab/v4/objects/issues.py @@ -2,7 +2,7 @@ from gitlab import cli from gitlab import exceptions as exc -from gitlab import types, utils +from gitlab import types from gitlab.base import RESTManager, RESTObject from gitlab.mixins import ( CreateMixin, @@ -272,7 +272,7 @@ def create( # type: ignore GitlabAuthenticationError: If authentication is not correct GitlabCreateError: If the server cannot perform the request """ - utils._validate_attrs(data=data, attributes=self._create_attrs) + self._create_attrs.validate_attrs(data=data) if TYPE_CHECKING: assert self.path is not None server_data = self.gitlab.http_post(self.path, post_data=data, **kwargs) diff --git a/tests/unit/mixins/test_mixin_methods.py b/tests/unit/mixins/test_mixin_methods.py index 8817cfd51..ed907e88f 100644 --- a/tests/unit/mixins/test_mixin_methods.py +++ b/tests/unit/mixins/test_mixin_methods.py @@ -3,7 +3,6 @@ from gitlab import base from gitlab import types as gl_types -from gitlab import utils from gitlab.mixins import ( CreateMixin, DeleteMixin, @@ -174,11 +173,11 @@ class M(CreateMixin, FakeManager): mgr = M(gl) data = {"foo": "bar", "baz": "blah"} - utils._validate_attrs(data=data, attributes=mgr._create_attrs) + mgr._create_attrs.validate_attrs(data=data) data = {"baz": "blah"} with pytest.raises(AttributeError) as error: - utils._validate_attrs(data=data, attributes=mgr._create_attrs) + mgr._create_attrs.validate_attrs(data=data) assert "foo" in str(error.value) @@ -240,11 +239,11 @@ class M(UpdateMixin, FakeManager): mgr = M(gl) data = {"foo": "bar", "baz": "blah"} - utils._validate_attrs(data=data, attributes=mgr._update_attrs) + mgr._update_attrs.validate_attrs(data=data) data = {"baz": "blah"} with pytest.raises(AttributeError) as error: - utils._validate_attrs(data=data, attributes=mgr._update_attrs) + mgr._update_attrs.validate_attrs(data=data) assert "foo" in str(error.value)