def __init__(self, config, paths=None, ignore=0, ignore_init_read_errors=False, allow_missing=False): self.config = config self.db_type = config['db_type'] self.ignore = ignore self.ignore_init_read_errors = ignore_init_read_errors self.mod_ctl = Modules(path=MOD_PATH, namepath='layman.db_modules', output=config['output']) self.output = config['output'] self.overlays = {} self.paths = paths path_found = False self.output.debug('Initializing overlay list handler', 8) if isinstance(self.db_type, STR): self.db_type = [x.strip() for x in self.db_type.split(',')] if len(self.db_type) > 1: msg = 'DbBase; warning, multiple instances of "db_type" found:'\ ' %(db_types)s.\nDefaulting to: %(db_type)s'\ % {'db_types': self.db_type, 'db_type': self.db_type[0]} self.output.warn(msg) self.db_type = self.db_type[0] + '_db' for path in self.paths: if not os.path.exists(path): continue success = self.read_db(path) if not success: msg = 'DbBase; error, Failed to read database at "%(path)s"\n'\ 'Hint: If you manually set db_type. Please reset it and '\ 'let layman-updater\nmigrate it. Otherwise layman\'s '\ 'database is not initialized, nor populated\nwith any '\ 'existing data.\nRun the following: "layman-updater -m '\ '<db_type>"' % {'path': path} self.output.error(msg) sys.exit(-1) path_found = True if not path_found and not allow_missing: msg = 'Warning: an installed db file was not found at: %(path)s'\ % {'path': str(self.paths)} self.output.warn(msg)
def __init__(self, config, json=None, ovl_dict=None, xml=None, ignore=0): self.config = config self.output = config['output'] self.module_controller = Modules(path=MOD_PATH, namepath='layman.overlays.modules', output=self.output) self._encoding_ = get_encoding(self.output) if xml is not None: self.from_xml(xml, ignore) elif ovl_dict is not None: self.from_dict(ovl_dict, ignore) elif json is not None: self.from_json(json, ignore)
def __init__(self, config, overlays): #TODO add custom_conf_type support self.config = config self.conf_types = config['conf_type'] self.output = config['output'] self.overlays = overlays self.module_controller = Modules(path=MOD_PATH, namepath='layman.config_modules', output=self.output) if isinstance(self.conf_types, STR): self.conf_types = [x.strip() for x in self.conf_types.split(',')] if not self.conf_types and self.config['require_repoconfig']: self.output.error('No Repo configuration type found, but' + '\nis required in order to continue...')
def __init__(self, config, xml=None, ovl_dict=None, ignore = 0): self.config = config self.output = config['output'] self.module_controller = Modules(path=MOD_PATH, namepath='layman.overlays.modules', output=self.output) self._encoding_ = get_encoding(self.output) if xml is not None: self.from_xml(xml, ignore) elif ovl_dict is not None: self.from_dict(ovl_dict, ignore)
def __init__(self, config, overlays): #TODO add custom_conf_type support self.config = config self.conf_types = config['conf_type'] self.output = config['output'] self.overlays = overlays self.module_controller = Modules(path=MOD_PATH, namepath='layman.config_modules', output=self.output) if isinstance(self.conf_types, STR): self.conf_types = re.split(',\s+', self.conf_types) if not self.conf_types and self.config['require_repoconfig']: self.output.error('No Repo configuration type found, but' + '\nis required in order to continue...')
def __init__(self, config, paths=None, ignore=0, ignore_init_read_errors=False, allow_missing=False): self.config = config self.db_type = config['db_type'] self.ignore = ignore self.ignore_init_read_errors = ignore_init_read_errors self.mod_ctl = Modules(path=MOD_PATH, namepath='layman.db_modules', output=config['output']) self.output = config['output'] self.overlays = {} self.paths = paths path_found = False self.output.debug('Initializing overlay list handler', 8) if isinstance(self.db_type, STR): self.db_type = [x.strip() for x in self.db_type.split(',')] if len(self.db_type) > 1: msg = 'DbBase; warning, multiple instances of "db_type" found:'\ ' %(db_types)s.\nDefaulting to: %(db_type)s'\ % {'db_types': self.db_type, 'db_type': self.db_type[0]} self.output.warn(msg) self.db_type = self.db_type[0] for path in self.paths: if not os.path.exists(path): continue self.read_db(path) path_found = True if not path_found and not allow_missing: msg = 'Warning: an installed db file was not found at: %(path)s'\ % {'path': str(self.paths)} self.output.warn(msg)
class Overlay(object): ''' Derive the real implementations from this.''' def __init__(self, config, json=None, ovl_dict=None, xml=None, ignore=0): self.config = config self.output = config['output'] self.module_controller = Modules(path=MOD_PATH, namepath='layman.overlays.modules', output=self.output) self._encoding_ = get_encoding(self.output) if xml is not None: self.from_xml(xml, ignore) elif ovl_dict is not None: self.from_dict(ovl_dict, ignore) elif json is not None: self.from_json(json, ignore) def __eq__(self, other): for i in ('descriptions', 'homepage', 'name', 'owners', '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 add(self, base): res = 1 first_s = True self.sources = self.filter_protocols(self.sources) if not self.sources: msg = 'Overlay.add() error: overlay "%(name)s" does not support '\ ' the given\nprotocol(s) %(protocol)s and cannot be '\ 'installed.'\ % {'name': self.name, 'protocol': str(self.config['protocol_filter'])} self.output.error(msg) return 1 for s in self.sources: if not first_s: self.output.info('\nTrying next source of listed sources...', 4) try: res = s.add(base) if res == 0: # Worked, throw other sources away self.sources = [s] break except Exception as error: self.output.warn(str(error), 4) first_s = False return res def delete(self, base): assert len(self.sources) == 1 return self.sources[0].delete(base) def filter_protocols(self, sources): ''' Filters any protocols not specified in self.config['protocol_filter'] from the overlay's sources. ''' _sources = [] if not self.config['protocol_filter']: return sources for source in sources: for protocol in self.config['protocol_filter']: protocol = protocol.lower() #re.search considers "\+" as the literal "+". if protocol == 'git+ssh': protocol = 'git\+ssh' protocol += '://' if re.search('^' + protocol, source.src): _sources.append(source) return _sources def from_dict(self, overlay, ignore): ''' Process an overlay dictionary definition ''' msg = 'Overlay from_dict(); overlay %(ovl)s' % {'ovl': str(overlay)} self.output.debug(msg, 6) _name = overlay['name'] if _name != None: self.name = encode(_name) else: msg = 'Overlay from_dict(), "name" entry missing from dictionary!' raise Exception(msg) if 'source' in overlay: _sources = overlay['source'] else: _sources = None if _sources == None: msg = 'Overlay from_dict(), "%(name)s" is missing a "source" '\ 'entry!' % {'name': self.name} raise Exception(msg) def create_dict_overlay_source(source_): _src, _type, _sub = source_ self.ovl_type = _type try: _class = self.module_controller.get_class(_type) except InvalidModuleName: _class = self.module_controller.get_class('stub') _location = encode(_src) if _sub: self.branch = encode(_sub) else: self.branch = None return _class(parent=self, config=self.config, _location=_location, ignore=ignore) self.sources = [create_dict_overlay_source(e) for e in _sources] self.owners = [] if 'owner' in overlay: for _owner in overlay['owner']: owner = {} if 'name' in _owner and _owner['name']: owner['name'] = encode(_owner['name']) else: owner['name'] = None if 'email' in _owner: owner['email'] = encode(_owner['email']) else: owner['email'] = None msg = 'Overlay from_dict(), "%(name)s" is missing an '\ '"owner.email" entry!' % {'name': self.name} if not ignore: raise Exception(msg) elif ignore == 1: self.output.warn(msg, 4) self.owners.append(owner) if 'description' in overlay: self.descriptions = [] _descs = overlay['description'] for d in _descs: d = WHITESPACE_REGEX.sub(' ', d) self.descriptions.append(encode(d)) else: self.descriptions = [''] if not ignore: raise Exception('Overlay from_dict(), "' + self.name + '" is missing a "description" entry!') elif ignore == 1: self.output.warn( 'Overlay from_dict(), "' + self.name + '" is missing a "description" entry!', 4) if 'status' in overlay: self.status = encode(overlay['status']) else: self.status = None self.quality = 'experimental' if 'quality' in overlay: if overlay['quality'] in set(QUALITY_LEVELS): self.quality = encode(overlay['quality']) if 'priority' in overlay: self.priority = int(overlay['priority']) else: self.priority = 50 if 'license' in overlay: self.license = encode(overlay['license']) else: self.license = None if 'homepage' in overlay: self.homepage = encode(overlay['homepage']) else: self.homepage = None if 'feed' in overlay: self.feeds = [encode(e) \ for e in overlay['feed']] else: self.feeds = None if 'irc' in overlay: self.irc = encode(overlay['irc']) else: self.irc = None # end of from_dict def from_json(self, json, ignore): ''' Process a json overlay definition ''' msg = 'Overlay from_json(); overlay %(ovl)s' % {'ovl': str(json)} self.output.debug(msg, 6) _name = json['name'] if _name != None: self.name = encode(_name) else: msg = 'Overlay from_json(), "name" entry missing from json!' raise Exception(msg) if 'source' in json: _sources = json['source'] else: _sources = None if _sources == None: msg = 'Overlay from_json(), "%(name)s" is missing a "source" '\ 'entry!' % {'name': self.name} raise Exception(msg) def create_json_overlay_source(source_): _src = source_['#text'] _type = source_['@type'] if '@branch' in source_: _sub = source_['@branch'] else: _sub = '' self.ovl_type = _type try: _class = self.module_controller.get_class(_type) except InvalidModuleName: _class = self.module_controller.get_class('stub') _location = encode(_src) if _sub: self.branch = encode(_sub) else: self.branch = None return _class(parent=self, config=self.config, _location=_location, ignore=ignore) self.sources = [create_json_overlay_source(e) for e in _sources] _owners = json['owner'] self.owners = [] for _owner in _owners: owner = {} if 'name' in _owner: owner['name'] = encode(_owner['name']) else: owner['name'] = None if 'email' in _owner: owner['email'] = encode(_owner['email']) else: owner['email'] = None msg = 'Overlay from_json(), "%(name)s" is missing an '\ '"owner.email" entry!' % {'name': self.name} if not ignore: raise Exception(msg) elif ignore == 1: self.output.warn(msg, 4) self.owners.append(owner) if 'description' in json: self.descriptions = [] _descs = json['description'] for d in _descs: d = WHITESPACE_REGEX.sub(' ', d) self.descriptions.append(encode(d)) else: self.descriptions = [''] msg = 'Overlay from_json() "%(name)s" is missing description'\ 'entry!' % {'name': self.name} if not ignore: raise Exception(msg) elif ignore == 1: self.output.warn(msg, 4) if '@status' in json: self.status = encode(json['@status']) else: self.status = None self.quality = 'experimental' if '@quality' in json: if json['@quality'] in set(QUALITY_LEVELS): self.quality = encode(json['@quality']) if '@priority' in json: self.priority = int(json['@priority']) else: self.priority = 50 if '@license' in json: self.license = encode(json['@license']) else: self.license = None if 'homepage' in json: self.homepage = encode(json['homepage']) else: self.homepage = None if 'feed' in json: self.feeds = [encode(e) \ for e in json['feed']] else: self.feeds = None if 'irc' in json: self.irc = encode(json['irc']) else: self.irc = None # end of from_json() def from_xml(self, xml, ignore): ''' Process an xml overlay definition ''' def strip_text(node): res = node.text if res is None: return '' return res.strip() _name = xml.find('name') if _name != None: self.name = encode(strip_text(_name)) elif 'name' in xml.attrib: self.name = encode(xml.attrib['name']) else: msg = 'Overlay from_xml(), "name" entry missing from xml!' raise Exception(msg) _sources = xml.findall('source') # new xml format if _sources != []: _sources = [e for e in _sources if 'type' in e.attrib] #old xml format 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 def create_overlay_source(source_elem): _branch = '' _type = source_elem.attrib['type'] self.ovl_type = _type if 'branch' in source_elem.attrib: _branch = source_elem.attrib['branch'] try: _class = self.module_controller.get_class(_type) except InvalidModuleName: _class = self.module_controller.get_class('stub') _location = encode(strip_text(source_elem)) self.branch = _branch return _class(parent=self, config=self.config, _location=_location, ignore=ignore) if not len(_sources): msg = 'Overlay from_xml(), "%(name)" is missing a "source" entry!'\ % {'name': self.name} raise Exception(msg) self.sources = [create_overlay_source(e) for e in _sources] _owners = xml.findall('owner') self.owners = [] # For backwards compatibility with older Overlay XML formats # default to this. if 'contact' in xml.attrib: owner = {'email': encode(xml.attrib['contact']), 'name': None} self.owners.append(owner) else: for _owner in _owners: owner = {} _email = _owner.find('email') _name = _owner.find('name') if _name != None: owner['name'] = encode(strip_text(_name)) else: owner['name'] = None if _email != None: owner['email'] = encode(strip_text(_email)) else: owner['email'] = None msg = 'Overlay from_xml(), "%(name)s" is missing an '\ '"owner.email" entry!' % {'name': self.name} if not ignore: raise Exception(msg) elif ignore == 1: self.output.warn(msg, 4) self.owners.append(owner) _desc = xml.findall('description') if _desc != None: self.descriptions = [] for d in _desc: d = WHITESPACE_REGEX.sub(' ', strip_text(d)) self.descriptions.append(encode(d)) else: self.descriptions = [''] msg = 'Overlay from_xml(), "%(name)s is missing a '\ '"description" entry!' % {'name': self.name} if not ignore: raise Exception(msg) elif ignore == 1: self.output.warn(msg, 4) if 'status' in xml.attrib: self.status = encode(xml.attrib['status']) else: self.status = None self.quality = 'experimental' if 'quality' in xml.attrib: if xml.attrib['quality'] in set(QUALITY_LEVELS): self.quality = encode(xml.attrib['quality']) if 'priority' in xml.attrib: self.priority = int(xml.attrib['priority']) else: self.priority = 50 if 'license' in xml.attrib: self.license = encode(xml.attrib['license']) else: self.license = None h = xml.find('homepage') l = xml.find('link') if h != None: self.homepage = encode(strip_text(h)) elif l != None: self.homepage = encode(strip_text(l)) else: self.homepage = None self.feeds = [encode(strip_text(e)) for e in xml.findall('feed')] _irc = xml.find('irc') if _irc != None: self.irc = encode(strip_text(_irc)) else: self.irc = None def get_infostr(self): ''' Gives more detailed string of overlay information. @rtype str: encoded overlay information. ''' result = '' result += self.name + '\n' + (len(self.name) * '~') if len(self.sources) == 1: result += '\nSource : ' + self.sources[0].src else: result += '\nSources : ' for i, v in enumerate(self.sources): result += '\n %d. %s' % (i + 1, v.src) result += '\n' if len(self.owners) == 1: if 'name' in self.owners[0] and self.owners[0]['name'] != None: result += '\nContact : %s <%s>' \ % (self.owners[0]['name'], self.owners[0]['email']) else: result += '\nContact : ' + self.owners[0]['email'] else: result += '\nContacts: ' for i, v in enumerate(self.owners): result += '\n %d. ' % (i + 1) if 'name' in v and v['name'] != None: result += '%s <%s>' % (v['name'], v['email']) else: result += v['email'] result += '\n' if len(self.sources) == 1: result += '\nType : ' + self.sources[0].type else: result += '\nType : ' + '/'.join( sorted(set(e.type for e in self.sources))) result += '; Priority: ' + str(self.priority) + '\n' result += 'Quality : ' + self.quality + '\n' for description in self.descriptions: description = re.compile(' +').sub(' ', description) description = re.compile('\n ').sub('\n', description) result += '\nDescription:' result += '\n '.join(('\n' + description).split('\n')) result += '\n' if self.homepage != None: link = self.homepage link = re.compile(' +').sub(' ', link) link = re.compile('\n ').sub('\n', link) result += '\nLink:' result += '\n '.join(('\n' + link).split('\n')) result += '\n' if self.irc != None: result += '\nIRC : ' + self.irc + '\n' if len(self.feeds): result += '\n%s:' % ((len(self.feeds) == 1) and "Feed" or "Feeds") for i in self.feeds: result += '\n %s' % i result += '\n' return encoder(result, self._encoding_) def is_supported(self): return any(e.is_supported() for e in self.sources) def set_priority(self, priority): ''' Set the priority of this overlay. ''' self.priority = int(priority) def short_list(self, width=0): ''' Return a shortened list of overlay information. @params width: int specifying terminal width. @rtype str: string of overlay information. ''' if len(self.name) > 25: name = self.name + " ###\n" name += pad(" ", 25) else: 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() - 1 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 source_types(self): for i in self.sources: yield i.type def is_official(self): ''' Is the overlay official? ''' return self.status == 'official' def source_uris(self): for i in self.sources: yield i.src def sync(self, base): msg = 'Overlay.sync(); name = %(name)s' % {'name': self.name} self.output.debug(msg, 4) assert len(self.sources) == 1 return self.sources[0].sync(base) def to_json(self): ''' Convert to json. ''' repo = {} repo['@priority'] = str(self.priority) repo['@quality'] = self.quality if self.status != None: repo['@status'] = self.status if self.license != None: repo['@license'] = self.license repo['name'] = self.name repo['description'] = [i for i in self.descriptions] if self.homepage != None: repo['homepage'] = self.homepage if self.irc != None: repo['irc'] = self.irc repo['owner'] = [i for i in self.owners] repo['source'] = [] for i in self.sources: source = {'@type': i.__class__.type_key} if i.branch: source['@branch'] = i.branch source['#text'] = i.src repo['source'].append(source) if self.feeds != None: repo['feed'] = [] for feed in self.feeds: repo['feed'].append(feed) return repo 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) if self.license != None: repo.attrib['license'] = self.license name = ET.Element('name') name.text = self.name repo.append(name) for i in self.descriptions: desc = ET.Element('description') desc.text = i repo.append(desc) del desc if self.homepage != None: homepage = ET.Element('homepage') homepage.text = self.homepage repo.append(homepage) if self.irc != None: irc = ET.Element('irc') irc.text = self.irc repo.append(irc) for _owner in self.owners: owner = ET.Element('owner') owner_email = ET.Element('email') owner_email.text = _owner['email'] owner.append(owner_email) if 'name' in _owner and _owner['name']: owner_name = ET.Element('name') owner_name.text = _owner['name'] owner.append(owner_name) repo.append(owner) for i in self.sources: if not i.branch: source = ET.Element('source', type=i.__class__.type_key) else: source = ET.Element('source', type=i.__class__.type_key, branch=i.branch) 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) if self.feeds != None: for i in self.feeds: feed = ET.Element('feed') feed.text = i repo.append(feed) del feed return repo def update(self, base, available_srcs): res = 1 first_src = True result = False self.sources = self.filter_protocols(self.sources) available_srcs = self.filter_protocols(available_srcs) if not self.sources or not available_srcs: msg = 'Overlay.update() error: overlay "%(name)s" does not support'\ ' the given protocol(s) %(protocol)s and cannot be updated.'\ % {'name': self.name, 'protocol': str(self.config['protocol_filter'])} self.output.error(msg) return 1 if isinstance(available_srcs, str): available_srcs = [available_srcs] if self.sources[0].type in self.config.get_option( 'support_url_updates'): for src in available_srcs: if not first_src: self.output.info( '\nTrying next source of listed sources...', 4) try: res = self.sources[0].update(base, src) if res == 0: # Updating it worked, no need to bother # checking other sources. self.sources[0].src = src result = True break except Exception as error: self.output.warn(str(error), 4) first_s = False else: # Update the overlay source with the remote # source, assuming that it's telling the truth # so it can be written to the installed.xml. msg = 'Overlay.update(); type: "%(src_type)s does not support '\ 'source URL updating' % {'src_type': self.sources[0].type} self.output.debug(msg, 4) self.sources[0].src = available_srcs.pop() result = True return (self.sources, result)
class Overlay(object): ''' Derive the real implementations from this.''' def __init__(self, config, json=None, ovl_dict=None, xml=None, ignore=0): self.config = config self.output = config['output'] self.module_controller = Modules(path=MOD_PATH, namepath='layman.overlays.modules', output=self.output) self._encoding_ = get_encoding(self.output) if xml is not None: self.from_xml(xml, ignore) elif ovl_dict is not None: self.from_dict(ovl_dict, ignore) elif json is not None: self.from_json(json, ignore) def __eq__(self, other): for i in ('descriptions', 'homepage', 'name', 'owners', '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 add(self, base): res = 1 first_s = True self.sources = self.filter_protocols(self.sources) if not self.sources: msg = 'Overlay.add() error: overlay "%(name)s" does not support '\ ' the given\nprotocol(s) %(protocol)s and cannot be '\ 'installed.'\ % {'name': self.name, 'protocol': str(self.config['protocol_filter'])} self.output.error(msg) return 1 for s in self.sources: if not first_s: self.output.info('\nTrying next source of listed sources...', 4) try: res = s.add(base) if res == 0: # Worked, throw other sources away self.sources = [s] break except Exception as error: self.output.warn(str(error), 4) first_s = False return res def delete(self, base): assert len(self.sources) == 1 return self.sources[0].delete(base) def filter_protocols(self, sources): ''' Filters any protocols not specified in self.config['protocol_filter'] from the overlay's sources. ''' _sources = [] if not self.config['protocol_filter']: return sources for source in sources: for protocol in self.config['protocol_filter']: protocol = protocol.lower() #re.search considers "\+" as the literal "+". if protocol == 'git+ssh': protocol = 'git\+ssh' protocol += '://' if re.search('^' + protocol, source.src): _sources.append(source) return _sources def from_dict(self, overlay, ignore): ''' Process an overlay dictionary definition ''' msg = 'Overlay from_dict(); overlay %(ovl)s' % {'ovl': str(overlay)} self.output.debug(msg, 6) _name = overlay['name'] if _name != None: self.name = encode(_name) else: msg = 'Overlay from_dict(), "name" entry missing from dictionary!' raise Exception(msg) if 'source' in overlay: _sources = overlay['source'] else: _sources = None if _sources == None: msg = 'Overlay from_dict(), "%(name)s" is missing a "source" '\ 'entry!' % {'name': self.name} raise Exception(msg) def create_dict_overlay_source(source_): _src, _type, _sub = source_ self.ovl_type = _type try: _class = self.module_controller.get_class(_type) except InvalidModuleName: _class = self.module_controller.get_class('stub') _location = encode(_src) if _sub: self.branch = encode(_sub) else: self.branch = None return _class(parent=self, config=self.config, _location=_location, ignore=ignore) self.sources = [create_dict_overlay_source(e) for e in _sources] self.owners = [] if 'owner' in overlay: for _owner in overlay['owner']: owner = {} if 'name' in _owner and _owner['name']: owner['name'] = encode(_owner['name']) else: owner['name'] = None if 'email' in _owner: owner['email'] = encode(_owner['email']) else: owner['email'] = None msg = 'Overlay from_dict(), "%(name)s" is missing an '\ '"owner.email" entry!' % {'name': self.name} if not ignore: raise Exception(msg) elif ignore == 1: self.output.warn(msg, 4) self.owners.append(owner) if 'description' in overlay: self.descriptions = [] _descs = overlay['description'] for d in _descs: d = WHITESPACE_REGEX.sub(' ', d) self.descriptions.append(encode(d)) else: self.descriptions = [''] if not ignore: raise Exception('Overlay from_dict(), "' + self.name + '" is missing a "description" entry!') elif ignore == 1: self.output.warn('Overlay from_dict(), "' + self.name + '" is missing a "description" entry!', 4) if 'status' in overlay: self.status = encode(overlay['status']) else: self.status = None self.quality = 'experimental' if 'quality' in overlay: if overlay['quality'] in set(QUALITY_LEVELS): self.quality = encode(overlay['quality']) if 'priority' in overlay: self.priority = int(overlay['priority']) else: self.priority = 50 if 'license' in overlay: self.license = encode(overlay['license']) else: self.license = None if 'homepage' in overlay: self.homepage = encode(overlay['homepage']) else: self.homepage = None if 'feed' in overlay: self.feeds = [encode(e) \ for e in overlay['feed']] else: self.feeds = None if 'irc' in overlay: self.irc = encode(overlay['irc']) else: self.irc = None # end of from_dict def from_json(self, json, ignore): ''' Process a json overlay definition ''' msg = 'Overlay from_json(); overlay %(ovl)s' % {'ovl': str(json)} self.output.debug(msg, 6) _name = json['name'] if _name != None: self.name = encode(_name) else: msg = 'Overlay from_json(), "name" entry missing from json!' raise Exception(msg) if 'source' in json: _sources = json['source'] else: _sources = None if _sources == None: msg = 'Overlay from_json(), "%(name)s" is missing a "source" '\ 'entry!' % {'name': self.name} raise Exception(msg) def create_json_overlay_source(source_): _src = source_['#text'] _type = source_['@type'] if '@branch' in source_: _sub = source_['@branch'] else: _sub = '' self.ovl_type = _type try: _class = self.module_controller.get_class(_type) except InvalidModuleName: _class = self.module_controller.get_class('stub') _location = encode(_src) if _sub: self.branch = encode(_sub) else: self.branch = None return _class(parent=self, config=self.config, _location=_location, ignore=ignore) self.sources = [create_json_overlay_source(e) for e in _sources] _owners = json['owner'] self.owners = [] for _owner in _owners: owner = {} if 'name' in _owner: owner['name'] = encode(_owner['name']) else: owner['name'] = None if 'email' in _owner: owner['email'] = encode(_owner['email']) else: owner['email'] = None msg = 'Overlay from_json(), "%(name)s" is missing an '\ '"owner.email" entry!' % {'name': self.name} if not ignore: raise Exception(msg) elif ignore == 1: self.output.warn(msg, 4) self.owners.append(owner) if 'description' in json: self.descriptions = [] _descs = json['description'] for d in _descs: d = WHITESPACE_REGEX.sub(' ', d) self.descriptions.append(encode(d)) else: self.descriptions = [''] msg = 'Overlay from_json() "%(name)s" is missing description'\ 'entry!' % {'name': self.name} if not ignore: raise Exception(msg) elif ignore == 1: self.output.warn(msg, 4) if '@status' in json: self.status = encode(json['@status']) else: self.status = None self.quality = 'experimental' if '@quality' in json: if json['@quality'] in set(QUALITY_LEVELS): self.quality = encode(json['@quality']) if '@priority' in json: self.priority = int(json['@priority']) else: self.priority = 50 if '@license' in json: self.license = encode(json['@license']) else: self.license = None if 'homepage' in json: self.homepage = encode(json['homepage']) else: self.homepage = None if 'feed' in json: self.feeds = [encode(e) \ for e in json['feed']] else: self.feeds = None if 'irc' in json: self.irc = encode(json['irc']) else: self.irc = None # end of from_json() def from_xml(self, xml, ignore): ''' Process an xml overlay definition ''' def strip_text(node): res = node.text if res is None: return '' return res.strip() _name = xml.find('name') if _name != None: self.name = encode(strip_text(_name)) elif 'name' in xml.attrib: self.name = encode(xml.attrib['name']) else: msg = 'Overlay from_xml(), "name" entry missing from xml!' raise Exception(msg) _sources = xml.findall('source') # new xml format if _sources != []: _sources = [e for e in _sources if 'type' in e.attrib] #old xml format 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 def create_overlay_source(source_elem): _branch = '' _type = source_elem.attrib['type'] self.ovl_type = _type if 'branch' in source_elem.attrib: _branch = source_elem.attrib['branch'] try: _class = self.module_controller.get_class(_type) except InvalidModuleName: _class = self.module_controller.get_class('stub') _location = encode(strip_text(source_elem)) self.branch = _branch return _class(parent=self, config=self.config, _location=_location, ignore=ignore) if not len(_sources): msg = 'Overlay from_xml(), "%(name)" is missing a "source" entry!'\ % {'name': self.name} raise Exception(msg) self.sources = [create_overlay_source(e) for e in _sources] _owners = xml.findall('owner') self.owners = [] # For backwards compatibility with older Overlay XML formats # default to this. if 'contact' in xml.attrib: owner = {'email': encode(xml.attrib['contact']), 'name': None} self.owners.append(owner) else: for _owner in _owners: owner = {} _email = _owner.find('email') _name = _owner.find('name') if _name != None: owner['name'] = encode(strip_text(_name)) else: owner['name'] = None if _email != None: owner['email'] = encode(strip_text(_email)) else: owner['email'] = None msg = 'Overlay from_xml(), "%(name)s" is missing an '\ '"owner.email" entry!' % {'name': self.name} if not ignore: raise Exception(msg) elif ignore == 1: self.output.warn(msg, 4) self.owners.append(owner) _desc = xml.findall('description') if _desc != None: self.descriptions = [] for d in _desc: d = WHITESPACE_REGEX.sub(' ', strip_text(d)) self.descriptions.append(encode(d)) else: self.descriptions = [''] msg = 'Overlay from_xml(), "%(name)s is missing a '\ '"description" entry!' % {'name': self.name} if not ignore: raise Exception(msg) elif ignore == 1: self.output.warn(msg, 4) if 'status' in xml.attrib: self.status = encode(xml.attrib['status']) else: self.status = None self.quality = 'experimental' if 'quality' in xml.attrib: if xml.attrib['quality'] in set(QUALITY_LEVELS): self.quality = encode(xml.attrib['quality']) if 'priority' in xml.attrib: self.priority = int(xml.attrib['priority']) else: self.priority = 50 if 'license' in xml.attrib: self.license = encode(xml.attrib['license']) else: self.license = None h = xml.find('homepage') l = xml.find('link') if h != None: self.homepage = encode(strip_text(h)) elif l != None: self.homepage = encode(strip_text(l)) else: self.homepage = None self.feeds = [encode(strip_text(e)) for e in xml.findall('feed')] _irc = xml.find('irc') if _irc != None: self.irc = encode(strip_text(_irc)) else: self.irc = None def get_infostr(self): ''' Gives more detailed string of overlay information. @rtype str: encoded overlay information. ''' result = '' result += self.name + '\n' + (len(self.name) * '~') if len(self.sources) == 1: result += '\nSource : ' + self.sources[0].src else: result += '\nSources : ' for i, v in enumerate(self.sources): result += '\n %d. %s' % (i + 1, v.src) result += '\n' if len(self.owners) == 1: if 'name' in self.owners[0] and self.owners[0]['name'] != None: result += '\nContact : %s <%s>' \ % (self.owners[0]['name'], self.owners[0]['email']) else: result += '\nContact : ' + self.owners[0]['email'] else: result += '\nContacts: ' for i, v in enumerate(self.owners): result += '\n %d. ' % (i + 1) if 'name' in v and v['name'] != None: result += '%s <%s>' % (v['name'], v['email']) else: result += v['email'] result += '\n' if len(self.sources) == 1: result += '\nType : ' + self.sources[0].type else: result += '\nType : ' + '/'.join( sorted(set(e.type for e in self.sources))) result += '; Priority: ' + str(self.priority) + '\n' result += 'Quality : ' + self.quality + '\n' for description in self.descriptions: description = re.compile(' +').sub(' ', description) description = re.compile('\n ').sub('\n', description) result += '\nDescription:' result += '\n '.join(('\n' + description).split('\n')) result += '\n' if self.homepage != None: link = self.homepage link = re.compile(' +').sub(' ', link) link = re.compile('\n ').sub('\n', link) result += '\nLink:' result += '\n '.join(('\n' + link).split('\n')) result += '\n' if self.irc != None: result += '\nIRC : ' + self.irc + '\n' if len(self.feeds): result += '\n%s:' % ((len(self.feeds) == 1) and "Feed" or "Feeds") for i in self.feeds: result += '\n %s' % i result += '\n' return encoder(result, self._encoding_) def is_supported(self): return any(e.is_supported() for e in self.sources) def set_priority(self, priority): ''' Set the priority of this overlay. ''' self.priority = int(priority) def short_list(self, width = 0): ''' Return a shortened list of overlay information. @params width: int specifying terminal width. @rtype str: string of overlay information. ''' if len(self.name) > 25: name = self.name + " ###\n" name += pad(" ", 25) else: 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()-1 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 source_types(self): for i in self.sources: yield i.type def is_official(self): ''' Is the overlay official? ''' return self.status == 'official' def source_uris(self): for i in self.sources: yield i.src def sync(self, base): msg = 'Overlay.sync(); name = %(name)s' % {'name': self.name} self.output.debug(msg, 4) assert len(self.sources) == 1 return self.sources[0].sync(base) def to_json(self): ''' Convert to json. ''' repo = {} repo['@priority'] = str(self.priority) repo['@quality'] = self.quality if self.status != None: repo['@status'] = self.status if self.license != None: repo['@license'] = self.license repo['name'] = self.name repo['description'] = [i for i in self.descriptions] if self.homepage != None: repo['homepage'] = self.homepage if self.irc != None: repo['irc'] = self.irc repo['owner'] = [i for i in self.owners] repo['source'] = [] for i in self.sources: source = {'@type': i.__class__.type_key} if i.branch: source['@branch'] = i.branch source['#text'] = i.src repo['source'].append(source) if self.feeds != None: repo['feed'] = [] for feed in self.feeds: repo['feed'].append(feed) return repo 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) if self.license != None: repo.attrib['license'] = self.license name = ET.Element('name') name.text = self.name repo.append(name) for i in self.descriptions: desc = ET.Element('description') desc.text = i repo.append(desc) del desc if self.homepage != None: homepage = ET.Element('homepage') homepage.text = self.homepage repo.append(homepage) if self.irc != None: irc = ET.Element('irc') irc.text = self.irc repo.append(irc) for _owner in self.owners: owner = ET.Element('owner') owner_email = ET.Element('email') owner_email.text = _owner['email'] owner.append(owner_email) if 'name' in _owner and _owner['name']: owner_name = ET.Element('name') owner_name.text = _owner['name'] owner.append(owner_name) repo.append(owner) for i in self.sources: if not i.branch: source = ET.Element('source', type=i.__class__.type_key) else: source = ET.Element('source', type=i.__class__.type_key, branch=i.branch) 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) if self.feeds != None: for i in self.feeds: feed = ET.Element('feed') feed.text = i repo.append(feed) del feed return repo def update(self, base, available_srcs): res = 1 first_src = True result = False self.sources = self.filter_protocols(self.sources) available_srcs = self.filter_protocols(available_srcs) if not self.sources or not available_srcs: msg = 'Overlay.update() error: overlay "%(name)s" does not support'\ ' the given protocol(s) %(protocol)s and cannot be updated.'\ % {'name': self.name, 'protocol': str(self.config['protocol_filter'])} self.output.error(msg) return 1 if isinstance(available_srcs, str): available_srcs = [available_srcs] if self.sources[0].type in self.config.get_option('support_url_updates'): for src in available_srcs: if not first_src: self.output.info('\nTrying next source of listed sources...', 4) try: res = self.sources[0].update(base, src) if res == 0: # Updating it worked, no need to bother # checking other sources. self.sources[0].src = src result = True break except Exception as error: self.output.warn(str(error), 4) first_s = False else: # Update the overlay source with the remote # source, assuming that it's telling the truth # so it can be written to the installed.xml. msg = 'Overlay.update(); type: "%(src_type)s does not support '\ 'source URL updating' % {'src_type': self.sources[0].type} self.output.debug(msg, 4) self.sources[0].src = available_srcs.pop() result = True return (self.sources, result)
class RepoConfManager: def __init__(self, config, overlays): #TODO add custom_conf_type support self.config = config self.conf_types = config['conf_type'] self.output = config['output'] self.overlays = overlays self.module_controller = Modules(path=MOD_PATH, namepath='layman.config_modules', output=self.output) if isinstance(self.conf_types, STR): self.conf_types = [x.strip() for x in self.conf_types.split(',')] if not self.conf_types and self.config['require_repoconfig']: self.output.error('No Repo configuration type found, but' + '\nis required in order to continue...') def add(self, overlay): ''' Adds overlay information to the specified config type(s). @param overlay: layman.overlay.Overlay instance. @return boolean: represents success or failure. ''' if self.config['require_repoconfig']: results = [] for types in self.conf_types: types = types.replace('.', '') conf = self.module_controller.get_class(types)\ (self.config, self.overlays) conf_ok = conf.add(overlay) results.append(conf_ok) return results return [True] def delete(self, overlay): ''' Deletes overlay information from the specified config type(s). @param overlay: layman.overlay.Overlay instance. @return boolean: represents success or failure. ''' if self.config['require_repoconfig']: results = [] for types in self.conf_types: types = types.replace('.', '') conf = self.module_controller.get_class(types)\ (self.config, self.overlays) conf_ok = conf.delete(overlay) results.append(conf_ok) return results return [True] def disable(self, overlay): ''' Allows an overlay to become no longer accessible to portage without deleting the overlay. @param overlay: layman.overlay.Overlay instance. @return boolean: represents success or failure. ''' if self.config['require_repoconfig']: for types in self.conf_types: types = types.replace('.', '') conf = self.module_controller.get_class(types)\ (self.config, self.overlays) conf_ok = conf.disable(overlay) return conf_ok return True def enable(self, overlay): ''' Allows an overlay to become accessible to portage after overlay was "forgotten". @param overlay: layman.overlay.Overlay instance. @return boolean: represents success or failure. ''' if self.config['require_repoconfig']: for types in self.conf_types: types = types.replace('.', '') conf = self.module_controller.get_class(types)\ (self.config, self.overlays) conf_ok = conf.enable(overlay) return conf_ok return True def update(self, overlay): ''' Updates the source URL for the specified config type(s). @param overlay: layman.overlay.Overlay instance. @return boolean: represents success or failure. ''' if self.config['require_repoconfig']: results = [] for types in self.conf_types: types = types.replace('.', '') conf = self.module_controller.get_class(types)\ (self.config, self.overlays) conf_ok = conf.update(overlay) results.append(conf_ok) return results return [True]
class DbBase(object): ''' Handle a list of overlays. ''' def __init__(self, config, paths=None, ignore=0, ignore_init_read_errors=False, allow_missing=False): self.config = config self.db_type = config['db_type'] self.ignore = ignore self.ignore_init_read_errors = ignore_init_read_errors self.mod_ctl = Modules(path=MOD_PATH, namepath='layman.db_modules', output=config['output']) self.output = config['output'] self.overlays = {} self.paths = paths path_found = False self.output.debug('Initializing overlay list handler', 8) if isinstance(self.db_type, STR): self.db_type = [x.strip() for x in self.db_type.split(',')] if len(self.db_type) > 1: msg = 'DbBase; warning, multiple instances of "db_type" found:'\ ' %(db_types)s.\nDefaulting to: %(db_type)s'\ % {'db_types': self.db_type, 'db_type': self.db_type[0]} self.output.warn(msg) self.db_type = self.db_type[0] for path in self.paths: if not os.path.exists(path): continue self.read_db(path) path_found = True if not path_found and not allow_missing: msg = 'Warning: an installed db file was not found at: %(path)s'\ % {'path': str(self.paths)} self.output.warn(msg) def __eq__(self, other): for key in set(self.overlays.keys()) | set(other.overlays.keys()): if self.overlays[key] != other.overlays[key]: return False return True def __ne__(self, other): return not self.__eq__(other) def _add_from_dict(self, overlays=None): ''' Add a new overlay from a list of dictionary values ''' self.output.info('DbBase: add_from_dict()') for overlay in overlays: self.output.debug('Parsing overlay entry', 8) ovl = Overlay(self.config, ovl_dict=overlay, ignore=self.ignore) self.overlays[ovl.name] = ovl return def _broken_catalog_hint(self): this_function_name = sys._getframe().f_code.co_name msg = 'Method "%(name)s.%(func)s" not implemented'\ % {'name': self.__class__.__name__, 'func': this_function_name} raise NotImplementedError(msg) def add_new(self, xml=None, origin=None, from_dict=None): ''' Reads xml text and dictionary definitions and adds them to the db. NOTE: Currently being refactored. Will be disabled until fixed. ''' ''' if xml is not None: self.read(xml, origin) if from_dict is not None: self.output.info("DbBase: add_new() from_dict") if isinstance(from_dict, dict): from_dict = [from_dict] self._add_from_dict(from_dict) ''' return def read_db(self, path, text=None, text_type=None): ''' Read the overlay database for installed overlay definitions. ''' if text and text_type: db_type = text_type else: db_type = self.db_type #Added to keep xml functionality for cached overlay XML definitions if 'cache' in path and '.xml' in path: db_type = 'xml_db' db_ctl = self.mod_ctl.get_class(db_type)(self.config, self.overlays, self.paths, self.ignore, self.ignore_init_read_errors) db_ctl.read_db(path, text=text) def write(self, path): ''' Write the list of overlays to a file. ''' db_ctl = self.mod_ctl.get_class(self.db_type)(self.config, self.overlays, self.paths, self.ignore, self.ignore_init_read_errors) db_ctl.write(path) def select(self, overlay): ''' Select an overlay from the list. ''' ovl = {'ovl': overlay} msg = 'DbBase.select(), overlay = %(ovl)s' % ovl self.output.debug(msg, 5) if not overlay in self.overlays.keys(): msg = 'DbBase.select(), unknown overlay = %(ovl)s' % ovl self.output.debug(msg, 4) ovls = {'ovls': ', '.join(self.overlays.keys())} msg = 'DbBase.select(), known overlays = %(ovls)s' % ovls self.output.debug(ovls, 4) raise UnknownOverlayException(overlay) return self.overlays[overlay] def list(self, repos=None, verbose=False, width=0): ''' List all overlays. ''' result = [] selection = [overlay for (a, overlay) in self.overlays.items()] if repos is not None: selection = [ovl for ovl in selection if ovl.name in repos] for overlay in selection: if verbose: result.append((overlay.get_infostr(), overlay.is_supported(), overlay.is_official())) else: result.append((overlay.short_list(width), overlay.is_supported(), overlay.is_official())) result = sorted(result, key=lambda summary_supported_official:\ summary_supported_official[0].lower()) return result def list_ids(self): ''' Returns a list of the overlay names ''' return sorted(self.overlays)
class DbBase(object): ''' Handle a list of overlays. ''' def __init__(self, config, paths=None, ignore=0, ignore_init_read_errors=False, allow_missing=False): self.config = config self.db_type = config['db_type'] self.ignore = ignore self.ignore_init_read_errors = ignore_init_read_errors self.mod_ctl = Modules(path=MOD_PATH, namepath='layman.db_modules', output=config['output']) self.output = config['output'] self.overlays = {} self.paths = paths path_found = False self.output.debug('Initializing overlay list handler', 8) if isinstance(self.db_type, STR): self.db_type = [x.strip() for x in self.db_type.split(',')] if len(self.db_type) > 1: msg = 'DbBase; warning, multiple instances of "db_type" found:'\ ' %(db_types)s.\nDefaulting to: %(db_type)s'\ % {'db_types': self.db_type, 'db_type': self.db_type[0]} self.output.warn(msg) self.db_type = self.db_type[0] + '_db' for path in self.paths: if not os.path.exists(path): continue success = self.read_db(path) if not success: msg = 'DbBase; error, Failed to read database at "%(path)s"\n'\ 'Hint: If you manually set db_type. Please reset it and '\ 'let layman-updater\nmigrate it. Otherwise layman\'s '\ 'database is not initialized, nor populated\nwith any '\ 'existing data.\nRun the following: "layman-updater -m '\ '<db_type>"' % {'path': path} self.output.error(msg) sys.exit(-1) path_found = True if not path_found and not allow_missing: msg = 'Warning: an installed db file was not found at: %(path)s'\ % {'path': str(self.paths)} self.output.warn(msg) def __eq__(self, other): for key in set(self.overlays.keys()) | set(other.overlays.keys()): if self.overlays[key] != other.overlays[key]: return False return True def __ne__(self, other): return not self.__eq__(other) def _add_from_dict(self, overlays=None): ''' Add a new overlay from a list of dictionary values ''' self.output.info('DbBase: add_from_dict()') for overlay in overlays: self.output.debug('Parsing overlay entry', 8) ovl = Overlay(self.config, ovl_dict=overlay, ignore=self.ignore) self.overlays[ovl.name] = ovl return def _broken_catalog_hint(self): this_function_name = sys._getframe().f_code.co_name msg = 'Method "%(name)s.%(func)s" not implemented'\ % {'name': self.__class__.__name__, 'func': this_function_name} raise NotImplementedError(msg) def _get_dbctl(self, db_type): ''' Returns database module controller for class or dies trying. ''' try: db_ctl = self.mod_ctl.get_class(db_type)(self.config, self.overlays, self.paths, self.ignore, self.ignore_init_read_errors) except InvalidModuleName: msg = 'DbBase._get_dbctl() error:\nDatabase module name '\ '"%(name)s" is invalid or not found.\nPlease set db_type '\ 'variable to proper value to continue.'\ % {'name': db_type.replace('_db', '')} self.output.die(msg) return db_ctl def add_new(self, xml=None, origin=None, from_dict=None): ''' Reads xml text and dictionary definitions and adds them to the db. NOTE: Currently being refactored. Will be disabled until fixed. ''' ''' if xml is not None: self.read(xml, origin) if from_dict is not None: self.output.info("DbBase: add_new() from_dict") if isinstance(from_dict, dict): from_dict = [from_dict] self._add_from_dict(from_dict) ''' return def read_db(self, path, text=None, text_type=None): ''' Read the overlay database for installed overlay definitions. ''' db_type = self.db_type if text and text_type: db_type = text_type + '_db' #Added to keep xml functionality for cached overlay XML definitions if 'cache' in path and '.xml' in path: db_type = 'xml_db' db_ctl = self._get_dbctl(db_type) return db_ctl.read_db(path, text=text) def write(self, path, remove=False, migrate_type=None): ''' Write the list of overlays to a file. ''' db_type = self.db_type if migrate_type: db_type = migrate_type + '_db' db_ctl = self._get_dbctl(db_type) db_ctl.write(path, remove=remove) def remove(self, overlay, path): ''' Remove an overlay from the database. ''' db_ctl = self._get_dbctl(self.db_type) db_ctl.remove(overlay, path) def select(self, overlay): ''' Select an overlay from the list. ''' ovl = {'ovl': overlay} msg = 'DbBase.select(), overlay = %(ovl)s' % ovl self.output.debug(msg, 5) if not overlay in self.overlays.keys(): msg = 'DbBase.select(), unknown overlay = %(ovl)s' % ovl self.output.debug(msg, 4) ovls = {'ovls': ', '.join(self.overlays.keys())} msg = 'DbBase.select(), known overlays = %(ovls)s' % ovls self.output.debug(ovls, 4) raise UnknownOverlayException(overlay) return self.overlays[overlay] def list(self, repos=None, verbose=False, width=0): ''' List all overlays. ''' result = [] selection = [overlay for (a, overlay) in self.overlays.items()] if repos is not None: selection = [ovl for ovl in selection if ovl.name in repos] for overlay in selection: if verbose: result.append((overlay.get_infostr(), overlay.is_supported(), overlay.is_official())) else: result.append((overlay.short_list(width), overlay.is_supported(), overlay.is_official())) result = sorted(result, key=lambda summary_supported_official:\ summary_supported_official[0].lower()) return result def list_ids(self): ''' Returns a list of the overlay names ''' return sorted(self.overlays)
class Overlay(object): ''' Derive the real implementations from this.''' def __init__(self, config, xml=None, ovl_dict=None, ignore = 0): self.config = config self.output = config['output'] self.module_controller = Modules(path=MOD_PATH, namepath='layman.overlays.modules', output=self.output) self._encoding_ = get_encoding(self.output) if xml is not None: self.from_xml(xml, ignore) elif ovl_dict is not None: self.from_dict(ovl_dict, ignore) def from_xml(self, xml, ignore): """Process an xml overlay definition """ def strip_text(node): res = node.text if res is None: return '' return res.strip() _name = xml.find('name') if _name != None: self.name = encode(strip_text(_name)) elif 'name' in xml.attrib: self.name = encode(xml.attrib['name']) else: raise Exception('Overlay from_xml(), "' + self.name + \ 'is missing a "name" entry!') _sources = xml.findall('source') # new xml format if _sources != []: _sources = [e for e in _sources if 'type' in e.attrib] #old xml format 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 def create_overlay_source(source_elem): _branch = '' _type = source_elem.attrib['type'] self.ovl_type = _type if 'branch' in source_elem.attrib: _branch = source_elem.attrib['branch'] try: _class = self.module_controller.get_class(_type) except InvalidModuleName: _class = self.module_controller.get_class('stub') _location = encode(strip_text(source_elem)) self.branch = _branch return _class(parent=self, config=self.config, _location=_location, ignore=ignore) if not len(_sources): raise Exception('Overlay from_xml(), "' + self.name + \ '" is missing a "source" entry!') 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 = encode(strip_text(_email)) _name = _owner.find('name') if _name != None: self.owner_name = encode(strip_text(_name)) else: self.owner_name = None elif 'contact' in xml.attrib: self.owner_email = encode(xml.attrib['contact']) self.owner_name = None else: self.owner_email = '' self.owner_name = None if not ignore: raise Exception('Overlay from_xml(), "' + self.name + \ '" is missing an "owner.email" entry!') elif ignore == 1: self.output.warn('Overlay "' + self.name + '" is missing a ' '"owner.email" entry!', 4) _desc = xml.findall('description') if _desc != None: self.descriptions = [] for d in _desc: d = WHITESPACE_REGEX.sub(' ', strip_text(d)) self.descriptions.append(encode(d)) else: self.descriptions = [''] if not ignore: raise Exception('Overlay from_xml(), "' + 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 = encode(xml.attrib['status']) else: self.status = None self.quality = 'experimental' if 'quality' in xml.attrib: if xml.attrib['quality'] in set(QUALITY_LEVELS): self.quality = encode(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 = encode(strip_text(h)) elif l != None: self.homepage = encode(strip_text(l)) else: self.homepage = None self.feeds = [encode(strip_text(e)) \ for e in xml.findall('feed')] _irc = xml.find('irc') if _irc != None: self.irc = encode(strip_text(_irc)) else: self.irc = None def from_dict(self, overlay, ignore): """Process an overlay dictionary definition """ self.output.debug("Overlay from_dict(); overlay" + str(overlay), 6) _name = overlay['name'] if _name != None: self.name = encode(_name) else: raise Exception('Overlay from_dict(), "' + self.name + 'is missing a "name" entry!') _sources = overlay['sources'] if _sources == None: raise Exception('Overlay from_dict(), "' + self.name + '" is missing a "source" entry!') def create_dict_overlay_source(source_): _src, _type, _sub = source_ self.ovl_type = _type try: _class = self.module_controller.get_class(_type) except InvalidModuleName: _class = self.module_controller.get_class('stub') _location = encode(_src) if _sub: self.branch = encode(_sub) else: self.branch = None return _class(parent=self, config=self.config, _location=_location, ignore=ignore) self.sources = [create_dict_overlay_source(e) for e in _sources] if 'owner_name' in overlay: _owner = overlay['owner_name'] self.owner_name = encode(_owner) else: self.owner_name = None if 'owner_email' in overlay: _email = overlay['owner_email'] self.owner_email = encode(_email) else: self.owner_email = None if not ignore: raise Exception('Overlay from_dict(), "' + self.name + '" is missing an "owner.email" entry!') elif ignore == 1: self.output.warn('Overlay from_dict(), "' + self.name + '" is missing an "owner.email" entry!', 4) if 'descriptions' in overlay: self.descriptions = [] _descs = overlay['descriptions'] for d in _descs: d = WHITESPACE_REGEX.sub(' ', d) self.descriptions.append(encode(d)) else: self.descriptions = [''] if not ignore: raise Exception('Overlay from_dict(), "' + self.name + '" is missing a "description" entry!') elif ignore == 1: self.output.warn('Overlay from_dict(), "' + self.name + '" is missing a "description" entry!', 4) if 'status' in overlay: self.status = encode(overlay['status']) else: self.status = None self.quality = 'experimental' if 'quality' in overlay: if overlay['quality'] in set(QUALITY_LEVELS): self.quality = encode(overlay['quality']) if 'priority' in overlay: self.priority = int(overlay['priority']) else: self.priority = 50 if 'homepage' in overlay: self.homepage = encode(overlay['homepage']) else: self.homepage = None if 'feeds' in overlay: self.feeds = [encode(e) \ for e in overlay['feeds']] else: self.feeds = None if 'irc' in overlay: self.irc = encode(overlay['irc']) else: self.irc = None #xml = self.to_xml() # end of from_dict def __eq__(self, other): for i in ('descriptions', '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) for i in self.descriptions: desc = ET.Element('description') desc.text = i repo.append(desc) del desc if self.homepage != None: homepage = ET.Element('homepage') homepage.text = self.homepage repo.append(homepage) if self.irc != None: irc = ET.Element('irc') irc.text = self.irc repo.append(irc) 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: if not i.branch: source = ET.Element('source', type=i.__class__.type_key) else: source = ET.Element('source', type=i.__class__.type_key, branch=i.branch) 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) if self.feeds != None: for i in self.feeds: feed = ET.Element('feed') feed.text = i repo.append(feed) del feed return repo def add(self, base): res = 1 first_s = True for s in self.sources: if not first_s: self.output.info("\nTrying next source of listed sources...", 4) try: res = s.add(base) if res == 0: # Worked, throw other sources away self.sources = [s] break except Exception as error: self.output.warn(str(error), 4) first_s = False return res def update(self, base, available_srcs): res = 1 first_src = True result = False if isinstance(available_srcs, str): available_srcs = [available_srcs] if self.sources[0].type in self.config.get_option('support_url_updates'): for src in available_srcs: if not first_src: self.output.info("\nTrying next source of listed sources...", 4) try: res = self.sources[0].update(base, src) if res == 0: # Updating it worked, no need to bother # checking other sources. self.sources[0].src = src result = True break except Exception as error: self.output.warn(str(error), 4) first_s = False else: # Update the overlay source with the remote # source, assuming that it's telling the truth # so it can be written to the installed.xml. self.output.debug("overlay.update(); type: %s does not support"\ " source URL updating" % self.sources[0].type, 4) self.sources[0].src = available_srcs.pop() result = True return (self.sources, result) def sync(self, base): self.output.debug("overlay.sync(); name = %s" % self.name, 4) assert len(self.sources) == 1 return self.sources[0].sync(base) def delete(self, base): assert len(self.sources) == 1 return self.sources[0].delete(base) def get_infostr(self): ''' Gives more detailed string of overlay information. @rtype str: encoded overlay information. ''' result = '' result += self.name + '\n' + (len(self.name) * '~') if len(self.sources) == 1: result += '\nSource : ' + self.sources[0].src else: result += '\nSources:' for i, v in enumerate(self.sources): result += '\n %d. %s' % (i + 1, v.src) result += '\n' if self.owner_name != None: result += '\nContact : %s <%s>' \ % (self.owner_name, self.owner_email) else: result += '\nContact : ' + self.owner_email if len(self.sources) == 1: result += '\nType : ' + self.sources[0].type else: result += '\nType : ' + '/'.join( sorted(set(e.type for e in self.sources))) result += '; Priority: ' + str(self.priority) + '\n' result += 'Quality : ' + self.quality + '\n' for description in self.descriptions: description = re.compile(' +').sub(' ', description) description = re.compile('\n ').sub('\n', description) result += '\nDescription:' result += '\n '.join(('\n' + description).split('\n')) result += '\n' if self.homepage != None: link = self.homepage link = re.compile(' +').sub(' ', link) link = re.compile('\n ').sub('\n', link) result += '\nLink:' result += '\n '.join(('\n' + link).split('\n')) result += '\n' if self.irc != None: result += '\nIRC : ' + self.irc + '\n' if len(self.feeds): result += '\n%s:' % ((len(self.feeds) == 1) and "Feed" or "Feeds") for i in self.feeds: result += '\n %s' % i result += '\n' return encoder(result, self._encoding_) def short_list(self, width = 0): ''' Return a shortened list of overlay information. @params width: int specifying terminal width. @rtype str: string of overlay information. ''' if len(self.name) > 25: name = self.name + " ###\n" name += pad(" ", 25) else: 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()-1 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
class RepoConfManager: def __init__(self, config, overlays): #TODO add custom_conf_type support self.config = config self.conf_types = config['conf_type'] self.output = config['output'] self.overlays = overlays self.module_controller = Modules(path=MOD_PATH, namepath='layman.config_modules', output=self.output) if isinstance(self.conf_types, STR): self.conf_types = re.split(',\s+', self.conf_types) if not self.conf_types and self.config['require_repoconfig']: self.output.error('No Repo configuration type found, but' + '\nis required in order to continue...') def add(self, overlay): ''' Adds overlay information to the specified config type(s). @param overlay: layman.overlay.Overlay instance. @return boolean: represents success or failure. ''' if self.config['require_repoconfig']: results = [] for types in self.conf_types: types = types.replace('.', '') conf = self.module_controller.get_class(types)\ (self.config, self.overlays) conf_ok = conf.add(overlay) results.append(conf_ok) return results return [True] def delete(self, overlay): ''' Deletes overlay information from the specified config type(s). @param overlay: layman.overlay.Overlay instance. @return boolean: represents success or failure. ''' if self.config['require_repoconfig']: results = [] for types in self.conf_types: types = types.replace('.', '') conf = self.module_controller.get_class(types)\ (self.config, self.overlays) conf_ok = conf.delete(overlay) results.append(conf_ok) return results return [True] def disable(self, overlay): ''' Allows an overlay to become no longer accessible to portage without deleting the overlay. @param overlay: layman.overlay.Overlay instance. @return boolean: represents success or failure. ''' if self.config['require_repoconfig']: for types in self.conf_types: types = types.replace('.', '') conf = self.module_controller.get_class(types)\ (self.config, self.overlays) conf_ok = conf.disable(overlay) return conf_ok return True def enable(self, overlay): ''' Allows an overlay to become accessible to portage after overlay was "forgotten". @param overlay: layman.overlay.Overlay instance. @return boolean: represents success or failure. ''' if self.config['require_repoconfig']: for types in self.conf_types: types = types.replace('.', '') conf = self.module_controller.get_class(types)\ (self.config, self.overlays) conf_ok = conf.enable(overlay) return conf_ok return True def update(self, overlay): ''' Updates the source URL for the specified config type(s). @param overlay: layman.overlay.Overlay instance. @return boolean: represents success or failure. ''' if self.config['require_repoconfig']: results = [] for types in self.conf_types: types = types.replace('.', '') conf = self.module_controller.get_class(types)\ (self.config, self.overlays) conf_ok = conf.update(overlay) results.append(conf_ok) return results return [True]