diff options
Diffstat (limited to 'gitlab/base.py')
-rw-r--r-- | gitlab/base.py | 38 |
1 files changed, 34 insertions, 4 deletions
diff --git a/gitlab/base.py b/gitlab/base.py index 5e5f57b..f7b52fa 100644 --- a/gitlab/base.py +++ b/gitlab/base.py @@ -16,9 +16,11 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import importlib +import textwrap from types import ModuleType from typing import Any, Dict, Iterable, NamedTuple, Optional, Tuple, Type +import gitlab from gitlab import types as g_types from gitlab.exceptions import GitlabParsingError @@ -32,6 +34,12 @@ __all__ = [ ] +_URL_ATTRIBUTE_ERROR = ( + f"https://python-gitlab.readthedocs.io/en/{gitlab.__version__}/" + f"faq.html#attribute-error-list" +) + + class RESTObject(object): """Represents an object built from server data. @@ -45,13 +53,20 @@ class RESTObject(object): _id_attr: Optional[str] = "id" _attrs: Dict[str, Any] + _created_from_list: bool # Indicates if object was created from a list() action _module: ModuleType _parent_attrs: Dict[str, Any] _short_print_attr: Optional[str] = None _updated_attrs: Dict[str, Any] manager: "RESTManager" - def __init__(self, manager: "RESTManager", attrs: Dict[str, Any]) -> None: + def __init__( + self, + manager: "RESTManager", + attrs: Dict[str, Any], + *, + created_from_list: bool = False, + ) -> None: if not isinstance(attrs, dict): raise GitlabParsingError( "Attempted to initialize RESTObject with a non-dictionary value: " @@ -64,6 +79,7 @@ class RESTObject(object): "_attrs": attrs, "_updated_attrs": {}, "_module": importlib.import_module(self.__module__), + "_created_from_list": created_from_list, } ) self.__dict__["_parent_attrs"] = self.manager.parent_attrs @@ -106,8 +122,22 @@ class RESTObject(object): except KeyError: try: return self.__dict__["_parent_attrs"][name] - except KeyError: - raise AttributeError(name) + except KeyError as exc: + message = ( + f"{type(self).__name__!r} object has no attribute {name!r}" + ) + if self._created_from_list: + message = ( + f"{message}\n\n" + + textwrap.fill( + f"{self.__class__!r} was created via a list() call and " + f"only a subset of the data may be present. To ensure " + f"all data is present get the object using a " + f"get(object.id) call. For more details, see:" + ) + + f"\n\n{_URL_ATTRIBUTE_ERROR}" + ) + raise AttributeError(message) from exc def __setattr__(self, name: str, value: Any) -> None: self.__dict__["_updated_attrs"][name] = value @@ -229,7 +259,7 @@ class RESTObjectList(object): def next(self) -> RESTObject: data = self._list.next() - return self._obj_cls(self.manager, data) + return self._obj_cls(self.manager, data, created_from_list=True) @property def current_page(self) -> int: |