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

Lines changed: 9 additions & 0 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 43 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy