diff options
Diffstat (limited to 'overlord/overlays/overlay.py')
-rw-r--r-- | overlord/overlays/overlay.py | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/overlord/overlays/overlay.py b/overlord/overlays/overlay.py new file mode 100644 index 0000000..4a3cae7 --- /dev/null +++ b/overlord/overlays/overlay.py @@ -0,0 +1,419 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# overlord OVERLAY BASE CLASS +################################################################################# +# File: overlay.py +# +# Base class for the different overlay types. +# +# Copyright: +# (c) 2005 - 2009 Gunnar Wrobel +# (c) 2009 Sebastian Pipping +# (c) 2009 Christian Groschupp +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel <wrobel@gentoo.org> +# Sebastian Pipping <sebastian@pipping.org> +# Christian Groschupp <christian@groschupp.org> +# +''' Basic overlay class.''' + +__version__ = "$Id: overlay.py 273 2006-12-30 15:54:50Z wrobel $" + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +import sys, re, os, os.path +import codecs +import locale +import xml.etree.ElementTree as ET # Python 2.5 + +from overlord.utils import (pad, terminal_width, get_encoding, encoder, + ensure_unicode) +#from overlord.debug import OUT + +from overlord.overlays.bzr import BzrOverlay +from overlord.overlays.darcs import DarcsOverlay +from overlord.overlays.git import GitOverlay +from overlord.overlays.g_common import GCommonOverlay +from overlord.overlays.mercurial import MercurialOverlay +from overlord.overlays.cvs import CvsOverlay +from overlord.overlays.svn import SvnOverlay +from overlord.overlays.rsync import RsyncOverlay +from overlord.overlays.tar import TarOverlay + +#=============================================================================== +# +# Constants +# +#------------------------------------------------------------------------------- + +OVERLAY_TYPES = dict((e.type_key, e) for e in ( + GitOverlay, + GCommonOverlay, + CvsOverlay, + SvnOverlay, + RsyncOverlay, + TarOverlay, + BzrOverlay, + MercurialOverlay, + DarcsOverlay +)) + +QUALITY_LEVELS = 'core|stable|testing|experimental|graveyard'.split('|') + +WHITESPACE_REGEX = re.compile('\s+') + +#=============================================================================== +# +# Class Overlay +# +#------------------------------------------------------------------------------- + +class Overlay(object): + ''' Derive the real implementations from this.''' + + def __init__(self, xml, config, ignore = 0, quiet = False): + ''' + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> import xml.etree.ElementTree as ET # Python 2.5 + >>> document = ET.parse(here + '/../tests/testfiles/global-overlays.xml') + >>> overlays = document.findall('overlay') + document.findall('repo') + >>> a = Overlay(overlays[0], dict()) + >>> a.name + u'wrobel' + >>> a.is_official() + True + >>> list(a.source_uris()) + [u'https://overlays.gentoo.org/svn/dev/wrobel'] + >>> a.owner_email + u'nobody@gentoo.org' + >>> a.description + u'Test' + >>> a.priority + 10 + >>> b = Overlay(overlays[1], dict()) + >>> b.is_official() + False + ''' + + self.output = config['output'] + self._encoding_ = get_encoding(self.output) + + def strip_text(node): + res = node.text + if res is None: + return '' + return res.strip() + + _name = xml.find('name') + if _name != None: + self.name = ensure_unicode(strip_text(_name)) + elif 'name' in xml.attrib: + self.name = ensure_unicode(xml.attrib['name']) + else: + raise Exception('Overlay is missing a "name" entry!') + + _sources = xml.findall('source') + if _sources: + _sources = [e for e in _sources if 'type' in e.attrib] + elif ('src' in xml.attrib) and ('type' in xml.attrib): + s = ET.Element('source', type=xml.attrib['type']) + s.text = xml.attrib['src'] + _sources = [s] + del s + + if not _sources: + raise Exception('Overlay "' + self.name + '" is missing a "source" entry!') + + + def create_overlay_source(source_elem): + _type = source_elem.attrib['type'] + try: + _class = OVERLAY_TYPES[_type] + except KeyError: + raise Exception('Unknown overlay type "%s"!' % _type) + _location = ensure_unicode(strip_text(source_elem)) + return _class(self, xml, config, _location, ignore, quiet) + + self.sources = [create_overlay_source(e) for e in _sources] + + + _owner = xml.find('owner') + if _owner == None: + _email = None + else: + _email = _owner.find('email') + if _owner != None and _email != None: + self.owner_email = ensure_unicode(strip_text(_email)) + _name = _owner.find('name') + if _name != None: + self.owner_name = ensure_unicode(strip_text(_name)) + else: + self.owner_name = None + elif 'contact' in xml.attrib: + self.owner_email = ensure_unicode(xml.attrib['contact']) + self.owner_name = None + else: + self.owner_email = '' + self.owner_name = None + if not ignore: + raise Exception('Overlay "' + self.name + '" is missing a ' + '"owner.email" entry!') + elif ignore == 1: + self.output.warn('Overlay "' + self.name + '" is missing a ' + '"owner.email" entry!', 4) + + + _desc = xml.find('description') + if _desc != None: + d = WHITESPACE_REGEX.sub(' ', strip_text(_desc)) + self.description = ensure_unicode(d) + del d + else: + self.description = '' + if not ignore: + raise Exception('Overlay "' + self.name + '" is missing a ' + '"description" entry!') + elif ignore == 1: + self.output.warn('Overlay "' + self.name + '" is missing a ' + '"description" entry!', 4) + + if 'status' in xml.attrib: + self.status = ensure_unicode(xml.attrib['status']) + else: + self.status = None + + self.quality = u'experimental' + if 'quality' in xml.attrib: + if xml.attrib['quality'] in set(QUALITY_LEVELS): + self.quality = ensure_unicode(xml.attrib['quality']) + + if 'priority' in xml.attrib: + self.priority = int(xml.attrib['priority']) + else: + self.priority = 50 + + h = xml.find('homepage') + l = xml.find('link') + if h != None: + self.homepage = ensure_unicode(strip_text(h)) + elif l != None: + self.homepage = ensure_unicode(strip_text(l)) + else: + self.homepage = None + + self.feeds = [ensure_unicode(strip_text(e)) for e in xml.findall('feed')] + + + def __eq__(self, other): + for i in ('description', 'homepage', 'name', 'owner_email', + 'owner_name', 'priority', 'status'): + if getattr(self, i) != getattr(other, i): + return False + for i in self.sources + other.sources: + if not i in self.sources: + return False + if not i in other.sources: + return False + return True + + def __ne__(self, other): + return not self.__eq__(other) + + def set_priority(self, priority): + '''Set the priority of this overlay.''' + + self.priority = int(priority) + + def to_xml(self): + '''Convert to xml.''' + + repo = ET.Element('repo') + if self.status != None: + repo.attrib['status'] = self.status + repo.attrib['quality'] = self.quality + repo.attrib['priority'] = str(self.priority) + name = ET.Element('name') + name.text = self.name + repo.append(name) + desc = ET.Element('description') + desc.text = self.description + repo.append(desc) + if self.homepage != None: + homepage = ET.Element('homepage') + homepage.text = self.homepage + repo.append(homepage) + owner = ET.Element('owner') + repo.append(owner) + owner_email = ET.Element('email') + owner_email.text = self.owner_email + owner.append(owner_email) + if self.owner_name != None: + owner_name = ET.Element('name') + owner_name.text = self.owner_name + owner.append(owner_name) + for i in self.sources: + source = ET.Element('source', type=i.__class__.type_key) + source.text = i.src + repo.append(source) + del source + for i in self.sources: + # NOTE: Two loops on purpose so the + # hooks are called with all sources in + i.to_xml_hook(repo) + for i in self.feeds: + feed = ET.Element('feed') + feed.text = i + repo.append(feed) + del feed + return repo + + def add(self, base, quiet = False): + res = 1 + for s in self.sources: + try: + res = s.add(base, quiet) + if res == 0: + # Worked, throw other sources away + self.sources = [s] + break + except Exception, error: + self.output.warn(str(error), 4) + return res + + def sync(self, base, quiet = False): + assert len(self.sources) == 1 + return self.sources[0].sync(base, quiet) + + def delete(self, base): + assert len(self.sources) == 1 + return self.sources[0].delete(base) + + def __str__(self): + ''' + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> import xml.etree.ElementTree as ET # Python 2.5 + >>> document = ET.parse(here + '/../tests/testfiles/global-overlays.xml') + >>> overlays = document.findall('overlay') + document.findall('repo') + >>> a = Overlay(overlays[0], dict()) + >>> print str(a) + wrobel + ~~~~~~ + Source : https://overlays.gentoo.org/svn/dev/wrobel + Contact : nobody@gentoo.org + Type : Subversion; Priority: 10 + Quality : experimental + <BLANKLINE> + Description: + Test + <BLANKLINE> + ''' + + result = u'' + + result += self.name + u'\n' + (len(self.name) * u'~') + + if len(self.sources) == 1: + result += u'\nSource : ' + self.sources[0].src + else: + result += u'\nSources:' + for i, v in enumerate(self.sources): + result += '\n %d. %s' % (i + 1, v.src) + result += '\n' + + if self.owner_name != None: + result += u'\nContact : %s <%s>' % (self.owner_name, self.owner_email) + else: + result += u'\nContact : ' + self.owner_email + if len(self.sources) == 1: + result += u'\nType : ' + self.sources[0].type + else: + result += u'\nType : ' + '/'.join(sorted(set(e.type for e in self.sources))) + result += u'; Priority: ' + str(self.priority) + u'\n' + result += u'Quality : ' + self.quality + u'\n' + + + description = self.description + description = re.compile(u' +').sub(u' ', description) + description = re.compile(u'\n ').sub(u'\n', description) + result += u'\nDescription:' + result += u'\n '.join((u'\n' + description).split(u'\n')) + result += u'\n' + + if self.homepage != None: + link = self.homepage + link = re.compile(u' +').sub(u' ', link) + link = re.compile(u'\n ').sub(u'\n', link) + result += u'\nLink:' + result += u'\n '.join((u'\n' + link).split(u'\n')) + result += u'\n' + + if self.feeds: + result += u'\n%s:' % ((len(self.feeds) == 1) and "Feed" or "Feeds") + for i in self.feeds: + result += u'\n %s' % i + result += u'\n' + + return encoder(result, self._encoding_) + + def short_list(self, width = 0): + ''' + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> import xml.etree.ElementTree as ET # Python 2.5 + >>> document = ET.parse(here + '/../tests/testfiles/global-overlays.xml') + >>> overlays = document.findall('repo') + document.findall('overlay') + >>> a = Overlay(overlays[0], dict()) + >>> print a.short_list(80) + wrobel [Subversion] (https://o.g.o/svn/dev/wrobel ) + ''' + + name = pad(self.name, 25) + + if len(set(e.type for e in self.sources)) == 1: + _type = self.sources[0].type + else: + _type = '%s/..' % self.sources[0].type + + mtype = ' [' + pad(_type, 10) + ']' + if not width: + width = terminal_width() + srclen = width - 43 + source = ', '.join(self.source_uris()) + if len(source) > srclen: + source = source.replace("overlays.gentoo.org", "o.g.o") + source = ' (' + pad(source, srclen) + ')' + + return encoder(name + mtype + source, self._encoding_) + + def is_official(self): + '''Is the overlay official?''' + + return self.status == 'official' + + def is_supported(self): + return any(e.is_supported() for e in self.sources) + + def source_uris(self): + for i in self.sources: + yield i.src + + def source_types(self): + for i in self.sources: + yield i.type + + +#================================================================================ +# +# Testing +# +#-------------------------------------------------------------------------------- + +if __name__ == '__main__': + import doctest + doctest.testmod(sys.modules[__name__]) |