summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNejc Habjan <nejc.habjan@siemens.com>2022-07-20 21:17:42 +0200
committerGitHub <noreply@github.com>2022-07-20 21:17:42 +0200
commit2c90fd0f317213a5a29bf6a2b63715a287e9fcfa (patch)
treeb856fc6cf2dfb93148a67655acd8054ecd177600
parentf6b6e18f96f4cdf67c8c53ae79e6a8259dcce9ee (diff)
parenta7e8cfbae8e53d2c4b1fb75d57d42f00db8abd81 (diff)
downloadgitlab-2c90fd0f317213a5a29bf6a2b63715a287e9fcfa.tar.gz
Merge pull request #2082 from python-gitlab/jlvillal/mark_lazy_state
chore: add a `lazy` boolean attribute to `RESTObject`
-rw-r--r--gitlab/base.py9
-rw-r--r--gitlab/mixins.py4
-rw-r--r--tests/unit/mixins/test_mixin_methods.py43
3 files changed, 54 insertions, 2 deletions
diff --git a/gitlab/base.py b/gitlab/base.py
index 920617b..dd59240 100644
--- a/gitlab/base.py
+++ b/gitlab/base.py
@@ -62,6 +62,7 @@ class RESTObject:
_parent_attrs: Dict[str, Any]
_repr_attr: Optional[str] = None
_updated_attrs: Dict[str, Any]
+ _lazy: bool
manager: "RESTManager"
def __init__(
@@ -70,6 +71,7 @@ class RESTObject:
attrs: Dict[str, Any],
*,
created_from_list: bool = False,
+ lazy: bool = False,
) -> None:
if not isinstance(attrs, dict):
raise GitlabParsingError(
@@ -84,6 +86,7 @@ class RESTObject:
"_updated_attrs": {},
"_module": importlib.import_module(self.__module__),
"_created_from_list": created_from_list,
+ "_lazy": lazy,
}
)
self.__dict__["_parent_attrs"] = self.manager.parent_attrs
@@ -137,6 +140,12 @@ class RESTObject:
)
+ f"\n\n{_URL_ATTRIBUTE_ERROR}"
)
+ elif self._lazy:
+ message = f"{message}\n\n" + textwrap.fill(
+ f"If you tried to access object attributes returned from the server, "
+ f"note that {self.__class__!r} was created as a `lazy` object and was "
+ f"not initialized with any data."
+ )
raise AttributeError(message)
def __setattr__(self, name: str, value: Any) -> None:
diff --git a/gitlab/mixins.py b/gitlab/mixins.py
index 519e83f..f33a1fc 100644
--- a/gitlab/mixins.py
+++ b/gitlab/mixins.py
@@ -135,11 +135,11 @@ class GetMixin(HeadMixin, _RestManagerBase):
if lazy is True:
if TYPE_CHECKING:
assert self._obj_cls._id_attr is not None
- return self._obj_cls(self, {self._obj_cls._id_attr: id})
+ return self._obj_cls(self, {self._obj_cls._id_attr: id}, lazy=lazy)
server_data = self.gitlab.http_get(path, **kwargs)
if TYPE_CHECKING:
assert not isinstance(server_data, requests.Response)
- return self._obj_cls(self, server_data)
+ return self._obj_cls(self, server_data, lazy=lazy)
class GetWithoutIdMixin(HeadMixin, _RestManagerBase):
diff --git a/tests/unit/mixins/test_mixin_methods.py b/tests/unit/mixins/test_mixin_methods.py
index c40ccbd..9121453 100644
--- a/tests/unit/mixins/test_mixin_methods.py
+++ b/tests/unit/mixins/test_mixin_methods.py
@@ -45,9 +45,52 @@ def test_get_mixin(gl):
assert isinstance(obj, FakeObject)
assert obj.foo == "bar"
assert obj.id == 42
+ assert obj._lazy is False
assert responses.assert_call_count(url, 1) is True
+def test_get_mixin_lazy(gl):
+ class M(GetMixin, FakeManager):
+ pass
+
+ url = "http://localhost/api/v4/tests/42"
+
+ mgr = M(gl)
+ with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
+ rsps.add(
+ method=responses.GET,
+ url=url,
+ json={"id": 42, "foo": "bar"},
+ status=200,
+ match=[responses.matchers.query_param_matcher({})],
+ )
+ obj = mgr.get(42, lazy=True)
+ assert isinstance(obj, FakeObject)
+ assert not hasattr(obj, "foo")
+ assert obj.id == 42
+ assert obj._lazy is True
+ # a `lazy` get does not make a network request
+ assert not rsps.calls
+
+
+def test_get_mixin_lazy_missing_attribute(gl):
+ class FakeGetManager(GetMixin, FakeManager):
+ pass
+
+ manager = FakeGetManager(gl)
+ obj = manager.get(1, lazy=True)
+ assert obj.id == 1
+ with pytest.raises(AttributeError) as exc:
+ obj.missing_attribute
+ # undo `textwrap.fill()`
+ message = str(exc.value).replace("\n", " ")
+ assert "'FakeObject' object has no attribute 'missing_attribute'" in message
+ assert (
+ "note that <class 'tests.unit.mixins.test_mixin_methods.FakeObject'> was "
+ "created as a `lazy` object and was not initialized with any data."
+ ) in message
+
+
@responses.activate
def test_head_mixin(gl):
class M(GetMixin, FakeManager):