summaryrefslogtreecommitdiff
path: root/gitlab/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'gitlab/base.py')
-rw-r--r--gitlab/base.py163
1 files changed, 163 insertions, 0 deletions
diff --git a/gitlab/base.py b/gitlab/base.py
index 0d82cf1..df25a36 100644
--- a/gitlab/base.py
+++ b/gitlab/base.py
@@ -531,3 +531,166 @@ class GitlabObject(object):
def __ne__(self, other):
return not self.__eq__(other)
+
+
+class RESTObject(object):
+ """Represents an object built from server data.
+
+ It holds the attributes know from te server, and the updated attributes in
+ another. This allows smart updates, if the object allows it.
+
+ You can redefine ``_id_attr`` in child classes to specify which attribute
+ must be used as uniq ID. ``None`` means that the object can be updated
+ without ID in the url.
+ """
+ _id_attr = 'id'
+
+ def __init__(self, manager, attrs):
+ self.__dict__.update({
+ 'manager': manager,
+ '_attrs': attrs,
+ '_updated_attrs': {},
+ '_module': importlib.import_module(self.__module__)
+ })
+ self.__dict__['_parent_attrs'] = self.manager.parent_attrs
+
+ # TODO(gpocentek): manage the creation of new objects from the received
+ # data (_constructor_types)
+
+ self._create_managers()
+
+ def __getattr__(self, name):
+ try:
+ return self.__dict__['_updated_attrs'][name]
+ except KeyError:
+ try:
+ return self.__dict__['_attrs'][name]
+ except KeyError:
+ try:
+ return self.__dict__['_parent_attrs'][name]
+ except KeyError:
+ raise AttributeError(name)
+
+ def __setattr__(self, name, value):
+ self.__dict__['_updated_attrs'][name] = value
+
+ def __str__(self):
+ data = self._attrs.copy()
+ data.update(self._updated_attrs)
+ return '%s => %s' % (type(self), data)
+
+ def __repr__(self):
+ if self._id_attr:
+ return '<%s %s:%s>' % (self.__class__.__name__,
+ self._id_attr,
+ self.get_id())
+ else:
+ return '<%s>' % self.__class__.__name__
+
+ def _create_managers(self):
+ managers = getattr(self, '_managers', None)
+ if managers is None:
+ return
+
+ for attr, cls_name in self._managers:
+ cls = getattr(self._module, cls_name)
+ manager = cls(self.manager.gitlab, parent=self)
+ self.__dict__[attr] = manager
+
+ def _update_attrs(self, new_attrs):
+ self.__dict__['_updated_attrs'] = {}
+ self.__dict__['_attrs'].update(new_attrs)
+
+ def get_id(self):
+ """Returns the id of the resource."""
+ if self._id_attr is None:
+ return None
+ return getattr(self, self._id_attr)
+
+
+class RESTObjectList(object):
+ """Generator object representing a list of RESTObject's.
+
+ This generator uses the Gitlab pagination system to fetch new data when
+ required.
+
+ Note: you should not instanciate such objects, they are returned by calls
+ to RESTManager.list()
+
+ Args:
+ manager: Manager to attach to the created objects
+ obj_cls: Type of objects to create from the json data
+ _list: A GitlabList object
+ """
+ def __init__(self, manager, obj_cls, _list):
+ """Creates an objects list from a GitlabList.
+
+ You should not create objects of this type, but use managers list()
+ methods instead.
+
+ Args:
+ manager: the RESTManager to attach to the objects
+ obj_cls: the class of the created objects
+ _list: the GitlabList holding the data
+ """
+ self.manager = manager
+ self._obj_cls = obj_cls
+ self._list = _list
+
+ def __iter__(self):
+ return self
+
+ def __len__(self):
+ return len(self._list)
+
+ def __next__(self):
+ return self.next()
+
+ def next(self):
+ data = self._list.next()
+ return self._obj_cls(self.manager, data)
+
+
+class RESTManager(object):
+ """Base class for CRUD operations on objects.
+
+ Derivated class must define ``_path`` and ``_obj_cls``.
+
+ ``_path``: Base URL path on which requests will be sent (e.g. '/projects')
+ ``_obj_cls``: The class of objects that will be created
+ """
+
+ _path = None
+ _obj_cls = None
+
+ def __init__(self, gl, parent=None):
+ """REST manager constructor.
+
+ Args:
+ gl (Gitlab): :class:`~gitlab.Gitlab` connection to use to make
+ requests.
+ parent: REST object to which the manager is attached.
+ """
+ self.gitlab = gl
+ self._parent = parent # for nested managers
+ self._computed_path = self._compute_path()
+
+ @property
+ def parent_attrs(self):
+ return self._parent_attrs
+
+ def _compute_path(self, path=None):
+ self._parent_attrs = {}
+ if path is None:
+ path = self._path
+ if self._parent is None or not hasattr(self, '_from_parent_attrs'):
+ return path
+
+ data = {self_attr: getattr(self._parent, parent_attr)
+ for self_attr, parent_attr in self._from_parent_attrs.items()}
+ self._parent_attrs = data
+ return path % data
+
+ @property
+ def path(self):
+ return self._computed_path