Skip to content

Commit 2c90fd0

Browse files
authored
Merge pull request #2082 from python-gitlab/jlvillal/mark_lazy_state
chore: add a `lazy` boolean attribute to `RESTObject`
2 parents f6b6e18 + a7e8cfb commit 2c90fd0

File tree

3 files changed

+54
-2
lines changed

3 files changed

+54
-2
lines changed

gitlab/base.py

+9
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ class RESTObject:
6262
_parent_attrs: Dict[str, Any]
6363
_repr_attr: Optional[str] = None
6464
_updated_attrs: Dict[str, Any]
65+
_lazy: bool
6566
manager: "RESTManager"
6667

6768
def __init__(
@@ -70,6 +71,7 @@ def __init__(
7071
attrs: Dict[str, Any],
7172
*,
7273
created_from_list: bool = False,
74+
lazy: bool = False,
7375
) -> None:
7476
if not isinstance(attrs, dict):
7577
raise GitlabParsingError(
@@ -84,6 +86,7 @@ def __init__(
8486
"_updated_attrs": {},
8587
"_module": importlib.import_module(self.__module__),
8688
"_created_from_list": created_from_list,
89+
"_lazy": lazy,
8790
}
8891
)
8992
self.__dict__["_parent_attrs"] = self.manager.parent_attrs
@@ -137,6 +140,12 @@ def __getattr__(self, name: str) -> Any:
137140
)
138141
+ f"\n\n{_URL_ATTRIBUTE_ERROR}"
139142
)
143+
elif self._lazy:
144+
message = f"{message}\n\n" + textwrap.fill(
145+
f"If you tried to access object attributes returned from the server, "
146+
f"note that {self.__class__!r} was created as a `lazy` object and was "
147+
f"not initialized with any data."
148+
)
140149
raise AttributeError(message)
141150

142151
def __setattr__(self, name: str, value: Any) -> None:

gitlab/mixins.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,11 @@ def get(
135135
if lazy is True:
136136
if TYPE_CHECKING:
137137
assert self._obj_cls._id_attr is not None
138-
return self._obj_cls(self, {self._obj_cls._id_attr: id})
138+
return self._obj_cls(self, {self._obj_cls._id_attr: id}, lazy=lazy)
139139
server_data = self.gitlab.http_get(path, **kwargs)
140140
if TYPE_CHECKING:
141141
assert not isinstance(server_data, requests.Response)
142-
return self._obj_cls(self, server_data)
142+
return self._obj_cls(self, server_data, lazy=lazy)
143143

144144

145145
class GetWithoutIdMixin(HeadMixin, _RestManagerBase):

tests/unit/mixins/test_mixin_methods.py

+43
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,52 @@ class M(GetMixin, FakeManager):
4545
assert isinstance(obj, FakeObject)
4646
assert obj.foo == "bar"
4747
assert obj.id == 42
48+
assert obj._lazy is False
4849
assert responses.assert_call_count(url, 1) is True
4950

5051

52+
def test_get_mixin_lazy(gl):
53+
class M(GetMixin, FakeManager):
54+
pass
55+
56+
url = "http://localhost/api/v4/tests/42"
57+
58+
mgr = M(gl)
59+
with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
60+
rsps.add(
61+
method=responses.GET,
62+
url=url,
63+
json={"id": 42, "foo": "bar"},
64+
status=200,
65+
match=[responses.matchers.query_param_matcher({})],
66+
)
67+
obj = mgr.get(42, lazy=True)
68+
assert isinstance(obj, FakeObject)
69+
assert not hasattr(obj, "foo")
70+
assert obj.id == 42
71+
assert obj._lazy is True
72+
# a `lazy` get does not make a network request
73+
assert not rsps.calls
74+
75+
76+
def test_get_mixin_lazy_missing_attribute(gl):
77+
class FakeGetManager(GetMixin, FakeManager):
78+
pass
79+
80+
manager = FakeGetManager(gl)
81+
obj = manager.get(1, lazy=True)
82+
assert obj.id == 1
83+
with pytest.raises(AttributeError) as exc:
84+
obj.missing_attribute
85+
# undo `textwrap.fill()`
86+
message = str(exc.value).replace("\n", " ")
87+
assert "'FakeObject' object has no attribute 'missing_attribute'" in message
88+
assert (
89+
"note that <class 'tests.unit.mixins.test_mixin_methods.FakeObject'> was "
90+
"created as a `lazy` object and was not initialized with any data."
91+
) in message
92+
93+
5194
@responses.activate
5295
def test_head_mixin(gl):
5396
class M(GetMixin, FakeManager):

0 commit comments

Comments
 (0)