8000 chore: attempt to be more informative for missing attributes · python-gitlab/python-gitlab@dafe0be · GitHub
[go: up one dir, main page]

Skip to content

Commit dafe0be

Browse files
chore: attempt to be more informative for missing attributes
A commonly reported issue from users on Gitter is that they get an AttributeError for an attribute that should be present. This is often caused due to the fact that they used the `list()` method to retrieve the object and objects retrieved this way often only have a subset of the full data. Add more details in the AttributeError message that explains the situation to users. This will hopefully allow them to resolve the issue.
1 parent 2cd15ac commit dafe0be

File tree

3 files changed

+39
-5
lines changed

3 files changed

+39
-5
lines changed

gitlab/base.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,19 @@ class RESTObject(object):
4545

4646
_id_attr: Optional[str] = "id"
4747
_attrs: Dict[str, Any]
48+
_list_created_object: bool # Indicates if object was created from a list() action
4849
_module: ModuleType
4950
_parent_attrs: Dict[str, Any]
5051
_short_print_attr: Optional[str] = None
5152
_updated_attrs: Dict[str, Any]
5253
manager: "RESTManager"
5354

54-
def __init__(self, manager: "RESTManager", attrs: Dict[str, Any]) -> None:
55+
def __init__(
56+
self,
57+
manager: "RESTManager",
58+
attrs: Dict[str, Any],
59+
list_created_object: bool = False,
60+
) -> None:
5561
if not isinstance(attrs, dict):
5662
raise GitlabParsingError(
5763
"Attempted to initialize RESTObject with a non-dictionary value: "
@@ -64,6 +70,7 @@ def __init__(self, manager: "RESTManager", attrs: Dict[str, Any]) -> None:
6470
"_attrs": attrs,
6571
"_updated_attrs": {},
6672
"_module": importlib.import_module(self.__module__),
73+
"_list_created_object": list_created_object,
6774
}
6875
)
6976
self.__dict__["_parent_attrs"] = self.manager.parent_attrs
@@ -107,7 +114,15 @@ def __getattr__(self, name: str) -> Any:
107114
try:
108115
return self.__dict__["_parent_attrs"][name]
109116
except KeyError:
110-
raise AttributeError(name)
117+
message = name
118+
if self._list_created_object:
119+
message = (
120+
f"Unable to find attribute {name!r}. This object was "
121+
f"created via a list() call and only a subset of the data "
122+
f"may be present. To ensure all data is present get the "
123+
f"object using a get(object.id) call"
124+
)
125+
raise AttributeError(message)
111126

112127
def __setattr__(self, name: str, value: Any) -> None:
113128
self.__dict__["_updated_attrs"][name] = value
@@ -225,7 +240,7 @@ def __next__(self) -> RESTObject:
225240

226241
def next(self) -> RESTObject:
227242
data = self._list.next()
228-
return self._obj_cls(self.manager, data)
243+
return self._obj_cls(manager=self.manager, attrs=data, list_created_object=True)
229244

230245
@property
231246
def current_page(self) -> int:

gitlab/mixins.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,12 @@ def list(self, **kwargs: Any) -> Union[base.RESTObjectList, List[base.RESTObject
240240
assert self._obj_cls is not None
241241
obj = self.gitlab.http_list(path, **data)
242242
if isinstance(obj, list):
243-
return [self._obj_cls(self, item) for item in obj]
243+
return [
244+
self._obj_cls(manager=self, attrs=item, list_created_object=True)
245+
for item in obj
246+
]
244247
else:
245-
return base.RESTObjectList(self, self._obj_cls, obj)
248+
return base.RESTObjectList(manager=self, obj_cls=self._obj_cls, _list=obj)
246249

247250

248251
class RetrieveMixin(ListMixin, GetMixin):

tests/unit/test_base.py

Lines changed: 16 additions & 0 deletions
< 954F /tr>
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,22 @@ def test_instantiate_non_dict(self, fake_gitlab, fake_manager):
9090
with pytest.raises(gitlab.exceptions.GitlabParsingError):
9191
FakeObject(fake_manager, ["a", "list", "fails"])
9292

93+
def test_missing_attribute(self, fake_gitlab, fake_manager):
94+
obj = FakeObject(manager=fake_manager, attrs={"foo": "bar"})
95+
with pytest.raises(AttributeError) as excinfo:
96+
obj.missing_attribute
97+
assert str(excinfo.value) == "missing_attribute"
98+
assert "object was created via a list()" not in str(excinfo.value)
99+
100+
# Test for more informative message if a list() created object
101+
obj = FakeObject(
102+
manager=fake_manager, attrs={"foo": "bar"}, list_created_object=True
103+
)
104+
with pytest.raises(AttributeError) as excinfo:
105+
obj.missing_attribute
106+
assert "missing_attribute" in str(excinfo.value)
107+
assert "object was created via a list()" in str(excinfo.value)
108+
93109
def test_picklability(self, fake_manager):
94110
obj = FakeObject(fake_manager, {"foo": "bar"})
95111
original_obj_module = obj._module

0 commit comments

Comments
 (0)
0