← Back to team overview

arsenal-user team mailing list archive

[RFC] lpltk Collection base class

 

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:


Follow ups