arsenal-user team mailing list archive
-
arsenal-user team
-
Mailing list archive
-
Message #00023
Re: [RFC] lpltk Collection base class
On 07/18/2012 01:26 PM, Bryce Harrington wrote:
> lpltk includes classes for objects and for object-collections.
> For example, Message and Messages or Bug and Bugs.
>
> All of the collection classes have certain functions defined, __iter__,
> __get__, et al. These are all more or less identical functions.
>
> I'd like to propose we establish a common (abstract) base class called
> Collection for all of these collection classes, that provides those
> identical functions.
>
> The benefits of doing this are a) to eliminate code duplication and thus
> make maintenace easier, and b) to reduce the amount of boilerplate
> needed when adding a new collection class. The net result of adding
> this class should be an overall reduction in line count.
>
> With this base class, a simple class like Distributions can be reduced
> from 45 lines to 8. It would look like this:
>
> from distribution import Distribution
>
> class Distributions(Collection):
> def __init__(self, lp):
> Collection.__init__(self, Distribution, lp)
>
> def _fetch_all(self):
> return self._service.launchpad.distributions
>
> For more complex classes like Bugs or BugTasks, the difference will not
> be as stark, but should still help eliminate redundancy.
>
> For classes that need to customize behaviors beyond what the base class
> does, all routines will be named with a single underscore so they can
> easily overridden as necessary.
>
> Below is my draft of the class. Questions and critique (and +1's)
> welcomed.
>
> Bryce
>
>
> #!/usr/bin/python
>
> from utils import typecheck_Collection
>
> class Collection(object):
> def __init__(self, tk_type, service, lp_objects=None):
> '''Initialize the instance from a Launchpad bug.'''
> self._tk_type = tk_type
> self._service = service
> self._lp_objects = None
>
> if lp_objects:
> self._lp_objects = typecheck_Collection(lp_objects)
>
> def __len__(self):
> return len(list(self.__iter__()))
>
> def __getitem__(self, key):
> self._fetch_if_needed()
> return self._tk_type(self._service, self._lp_objects[key])
>
> def __iter__(self):
> self._fetch_if_needed()
> for obj in self._lp_objects:
> o = self._get(obj)
> yield o
>
> def __contains__(self, item):
> return item in self.__iter__()
>
> def _fetch_if_needed(self):
> if self._lp_objects == None:
> self._lp_objects = self._fetch_all()
> assert(self._lp_objects is not None)
>
> def _get(self, obj):
> return self._tk_type(self._service, obj)
>
> def _fetch_all(self):
> '''Override this routine in the subclass to retrieve the necessary
> data from launchpad and return it as a LPCollection of LP objects.'''
> import inspect
> raise TypeError('Abstract method `%s.%s` called' %(
> type(self).__name__, inspect.stack()[0][3]))
>
>
> if __name__ == "__main__":
> from lpltk import LaunchpadService
> from distribution import Distribution
>
> class MyDists(Collection):
> def __init__(self, lp):
> Collection.__init__(self, Distribution, lp)
>
> def _fetch_all(self):
> return self._service.launchpad.distributions
>
> lp = LaunchpadService(config={'read_only':True})
> objects = MyDists(lp)
> for obj in objects:
> print obj.display_name,
> if obj.current_series:
> print obj.current_series.name
> else:
> print "<no series>"
>
> # vi:set ts=4 sw=4 expandtab:
>
Makes sense to me. I don't see any problems with this.
--
Brad Figg brad.figg@xxxxxxxxxxxxx http://www.canonical.com
Follow ups
References