From d508fabc6f1a8917861ff8b960315eee2af9dc55 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 19 Jan 2007 11:42:40 +0100 Subject: * move the distro handling into its own file (distro.py) * rename DistInfo to distinfo, since it contains more than only one class * Use the term template instead of suite in distinfo * fix the test function --- aptsources/DistInfo.py | 238 -------------------------- aptsources/aptsources.py | 407 +-------------------------------------------- aptsources/distinfo.py | 241 +++++++++++++++++++++++++++ aptsources/distro.py | 422 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 667 insertions(+), 641 deletions(-) delete mode 100644 aptsources/DistInfo.py create mode 100644 aptsources/distinfo.py create mode 100644 aptsources/distro.py (limited to 'aptsources') diff --git a/aptsources/DistInfo.py b/aptsources/DistInfo.py deleted file mode 100644 index 69bc3410..00000000 --- a/aptsources/DistInfo.py +++ /dev/null @@ -1,238 +0,0 @@ -#!/usr/bin/env python -# DistInfo.py - simple parser for a xml-based metainfo file -# -# Copyright (c) 2005 Gustavo Noronha Silva -# -# Author: Gustavo Noronha Silva -# Sebastian Heinlein -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA - -import os -import gettext -from os import getenv -import ConfigParser -import string - -from gettext import gettext as _ - -import re -class Suite: - def __init__(self): - self.name = None - self.child = False - self.match_name = None - self.description = None - self.base_uri = None - self.type = None - self.components = [] - self.children = [] - self.match_uri = None - self.mirror_set = {} - self.distribution = None - self.available = True - - def has_component(self, comp): - ''' Check if the distribution provides the given component ''' - return comp in map(lambda c: c.name, self.components) - - def is_mirror(self, url): - ''' Check if a given url of a repository is a valid mirror ''' - proto, hostname, dir = split_url(url) - if self.mirror_set.has_key(hostname): - return self.mirror_set[hostname].has_repository(proto, dir) - else: - return False - -class Component: - def __init__(self, name, desc=None, long_desc=None): - self.name = name - self.description = desc - self.description_long = long_desc - def get_description(self): - if self.description_long != None: - return self.description_long - elif self.description != None: - return self.description - else: - return None - def set_description(self, desc): - self.description = desc - def set_description_long(self, desc): - self.description_long = desc - def get_description_long(self): - return self.description_long - -class Mirror: - ''' Storage for mirror related information ''' - def __init__(self, proto, hostname, dir, location=None): - self.hostname = hostname - self.repositories = [] - self.add_repository(proto, dir) - self.location = location - def add_repository(self, proto, dir): - self.repositories.append(Repository(proto, dir)) - def get_repositories_for_proto(self, proto): - return filter(lambda r: r.proto == proto, self.repositories) - def has_repository(self, proto, dir): - return len(filter(lambda r: (r.proto == proto) and dir in r.dir, - self.repositories)) > 0 - def get_repo_urls(self): - return map(lambda r: r.get_url(self.hostname), self.repositories) - def get_location(self): - return self.location - def set_location(self, location): - self.location = location - -class Repository: - def __init__(self, proto, dir): - self.proto = proto - self.dir = dir - def get_info(self): - return self.proto, self.dir - def get_url(self, hostname): - return "%s://%s/%s" % (self.proto, hostname, self.dir) - -def split_url(url): - ''' split a given URL into the protocoll, the hostname and the dir part ''' - return map(lambda a,b: a, re.split(":*\/+", url, maxsplit=2), - [None, None, None]) - -class DistInfo: - def __init__(self, - dist = None, - base_dir = "/usr/share/python-aptsources/templates"): - self.metarelease_uri = '' - self.suites = [] - - location = None - match_loc = re.compile(r"^#LOC:(.+)$") - match_mirror_line = re.compile(r"^(#LOC:.+)|(((http)|(ftp)|(rsync)|(file)|(https))://[A-Za-z/\.:\-_]+)$") - #match_mirror_line = re.compile(r".+") - - if not dist: - pipe = os.popen("lsb_release -i -s") - dist = pipe.read().strip() - pipe.close() - del pipe - - self.dist = dist - - - map_mirror_sets = {} - - dist_fname = "%s/%s.info" % (base_dir, dist) - dist_file = open (dist_fname) - if not dist_file: - return - suite = None - component = None - for line in dist_file: - tokens = line.split (':', 1) - if len (tokens) < 2: - continue - field = tokens[0].strip () - value = tokens[1].strip () - if field == 'ChangelogURI': - self.changelogs_uri = _(value) - elif field == 'MetaReleaseURI': - self.metarelease_uri = value - elif field == 'Suite': - if suite: - if component and not suite.has_component(component.name): - suite.components.append(component) - component = None - self.suites.append (suite) - suite = Suite () - suite.name = value - suite.distribution = dist - suite.match_name = "^%s$" % value - elif field == 'MatchName': - suite.match_name = value - elif field == 'ParentSuite': - suite.child = True - for nanny in self.suites: - if nanny.name == value: - nanny.children.append(suite) - # reuse some properties of the parent suite - if suite.match_uri == None: - suite.match_uri = nanny.match_uri - if suite.mirror_set == {}: - suite.mirror_set = nanny.mirror_set - if suite.base_uri == None: - suite.base_uri = nanny.base_uri - elif field == 'Available': - suite.available = value - elif field == 'RepositoryType': - suite.type = value - elif field == 'BaseURI': - suite.base_uri = value - suite.match_uri = value - elif field == 'MatchURI': - suite.match_uri = value - elif field == 'MirrorsFile': - if not map_mirror_sets.has_key(value): - mirror_set = {} - try: - mirror_data = filter(match_mirror_line.match, - map(string.strip, open(value))) - except: - print "ERROR: Failed to read mirror file" - mirrors = [] - for line in mirror_data: - if line.startswith("#LOC:"): - location = match_loc.sub(r"\1", line) - continue - (proto, hostname, dir) = split_url(line) - if mirror_set.has_key(hostname): - mirror_set[hostname].add_repository(proto, dir) - else: - mirror_set[hostname] = Mirror(proto, hostname, dir, location) - map_mirror_sets[value] = mirror_set - suite.mirror_set = map_mirror_sets[value] - elif field == 'Description': - suite.description = _(value) - elif field == 'Component': - if component and not suite.has_component(component.name): - suite.components.append(component) - component = Component(value) - elif field == 'CompDescription': - component.set_description(_(value)) - elif field == 'CompDescriptionLong': - component.set_description_long(_(value)) - if suite: - if component: - suite.components.append(component) - component = None - self.suites.append (suite) - suite = None - -if __name__ == "__main__": - d = DistInfo ("Ubuntu", "/usr/share/python-aptsources/templates") - print d.changelogs_uri - for suite in d.suites: - print "\nSuite: %s" % suite.name - print "Desc: %s" % suite.description - print "BaseURI: %s" % suite.base_uri - print "MatchURI: %s" % suite.match_uri - if suite.mirror_set != {}: - print "Mirrors: %s" % suite.mirror_set.keys() - for comp in suite.components: - print " %s -%s -%s" % (comp.name, - comp.description, - comp.short_description) - for child in suite.children: - print " %s" % child.description diff --git a/aptsources/aptsources.py b/aptsources/aptsources.py index 457e13f2..0928a0e8 100644 --- a/aptsources/aptsources.py +++ b/aptsources/aptsources.py @@ -1,12 +1,12 @@ -# aptsource.py.in - parse sources.list +# aptsource.py - Provide an abstraction of the sources.list # -# Copyright (c) 2004-2006 Canonical +# Copyright (c) 2004-2007 Canonical Ltd. # 2004 Michiel Sikkes -# 2006 Sebastian Heinlein +# 2006-2007 Sebastian Heinlein # # Author: Michiel Sikkes # Michael Vogt -# Sebastian Heinlein +# Sebastian Heinlein # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as @@ -407,405 +407,6 @@ class SourceEntryMatcher: break return found -class Distribution: - def __init__(self, id, codename, description, release): - """ Container for distribution specific informations """ - # LSB information - self.id = id - self.codename = codename - self.description = description - self.release = release - - self.binary_type = "deb" - self.source_type = "deb-src" - - def get_sources(self, sourceslist): - """ - Find the corresponding template, main and child sources - for the distribution - """ - - self.sourceslist = sourceslist - # corresponding sources - self.source_template = None - self.child_sources = [] - self.main_sources = [] - self.disabled_sources = [] - self.cdrom_sources = [] - self.download_comps = [] - self.enabled_comps = [] - self.cdrom_comps = [] - self.used_media = [] - self.get_source_code = False - self.source_code_sources = [] - - # location of the sources - self.default_server = "" - self.main_server = "" - self.nearest_server = "" - self.used_servers = [] - - # find the distro template - for template in self.sourceslist.matcher.templates: - if self.is_codename(template.name) and\ - template.distribution == self.id: - #print "yeah! found a template for %s" % self.description - #print template.description, template.base_uri, template.components - self.source_template = template - break - if self.source_template == None: - print "Error: could not find a distribution template" - # FIXME: will go away - only for debugging issues - sys.exit(1) - - # find main and child sources - media = [] - comps = [] - cdrom_comps = [] - enabled_comps = [] - source_code = [] - for source in self.sourceslist.list: - if source.invalid == False and\ - self.is_codename(source.dist) and\ - source.template and\ - self.is_codename(source.template.name): - #print "yeah! found a distro repo: %s" % source.line - # cdroms need do be handled differently - if source.uri.startswith("cdrom:") and \ - source.disabled == False: - self.cdrom_sources.append(source) - cdrom_comps.extend(source.comps) - elif source.uri.startswith("cdrom:") and \ - source.disabled == True: - self.cdrom_sources.append(source) - elif source.type == self.binary_type and \ - source.disabled == False: - self.main_sources.append(source) - comps.extend(source.comps) - media.append(source.uri) - elif source.type == self.binary_type and \ - source.disabled == True: - self.disabled_sources.append(source) - elif source.type == self.source_type and source.disabled == False: - self.source_code_sources.append(source) - elif source.type == self.source_type and source.disabled == True: - self.disabled_sources.append(source) - if source.invalid == False and\ - source.template in self.source_template.children: - if source.disabled == False and source.type == self.binary_type: - self.child_sources.append(source) - elif source.disabled == False and source.type == self.source_type: - self.source_code_sources.append(source) - else: - self.disabled_sources.append(source) - self.download_comps = set(comps) - self.cdrom_comps = set(cdrom_comps) - enabled_comps.extend(comps) - enabled_comps.extend(cdrom_comps) - self.enabled_comps = set(enabled_comps) - self.used_media = set(media) - - self.get_mirrors() - - def get_mirrors(self): - """ - Provide a set of mirrors where you can get the distribution from - """ - # the main server is stored in the template - self.main_server = self.source_template.base_uri - - # other used servers - for medium in self.used_media: - if not medium.startswith("cdrom:"): - # seems to be a network source - self.used_servers.append(medium) - - if len(self.main_sources) == 0: - self.default_server = self.main_server - else: - self.default_server = self.main_sources[0].uri - - def add_source(self, type=None, - uri=None, dist=None, comps=None, comment=""): - """ - Add distribution specific sources - """ - if uri == None: - # FIXME: Add support for the server selector - uri = self.default_server - if dist == None: - dist = self.codename - if comps == None: - comps = list(self.enabled_comps) - if type == None: - type = self.binary_type - new_source = self.sourceslist.add(type, uri, dist, comps, comment) - # if source code is enabled add a deb-src line after the new - # source - if self.get_source_code == True and tpye == self.binary_type: - self.sourceslist.add(self.source_type, uri, dist, comps, comment, - file=new_source.file, - pos=self.sourceslist.list.index(new_source)+1) - - def enable_component(self, comp): - """ - Enable a component in all main, child and source code sources - (excluding cdrom based sources) - - sourceslist: an aptsource.sources_list - comp: the component that should be enabled - """ - def add_component_only_once(source, comps_per_dist): - """ - Check if we already added the component to the repository, since - a repository could be splitted into different apt lines. If not - add the component - """ - # if we don't that distro, just reutnr (can happen for e.g. - # dapper-update only in deb-src - if not comps_per_dist.has_key(source.dist): - return - # if we have seen this component already for this distro, - # return (nothing to do - if comp in comps_per_dist[source.dist]: - return - # add it - source.comps.append(comp) - comps_per_dist[source.dist].add(comp) - - sources = [] - sources.extend(self.main_sources) - sources.extend(self.child_sources) - # store what comps are enabled already per distro (where distro is - # e.g. "dapper", "dapper-updates") - comps_per_dist = {} - comps_per_sdist = {} - for s in sources: - if s.type == self.binary_type: - if not comps_per_dist.has_key(s.dist): - comps_per_dist[s.dist] = set() - map(comps_per_dist[s.dist].add, s.comps) - for s in self.source_code_sources: - if s.type == self.source_type: - if not comps_per_sdist.has_key(s.dist): - comps_per_sdist[s.dist] = set() - map(comps_per_sdist[s.dist].add, s.comps) - - # check if there is a main source at all - if len(self.main_sources) < 1: - # create a new main source - self.add_source(comps=["%s"%comp]) - else: - # add the comp to all main, child and source code sources - for source in sources: - add_component_only_once(source, comps_per_dist) - - # check if there is a main source code source at all - if self.get_source_code == True: - if len(self.source_code_sources) < 1: - # create a new main source - self.add_source(type=self.source_type, comps=["%s"%comp]) - else: - # add the comp to all main, child and source code sources - for source in self.source_code_sources: - add_component_only_once(source, comps_per_sdist) - - def disable_component(self, comp): - """ - Disable a component in all main, child and source code sources - (excluding cdrom based sources) - """ - sources = [] - sources.extend(self.main_sources) - sources.extend(self.child_sources) - sources.extend(self.source_code_sources) - if comp in self.cdrom_comps: - sources = [] - sources.extend(self.main_sources) - - for source in sources: - if comp in source.comps: - source.comps.remove(comp) - if len(source.comps) < 1: - self.sourceslist.remove(source) - - def change_server(self, uri): - ''' Change the server of all distro specific sources to - a given host ''' - sources = [] - seen = [] - self.default_server = uri - sources.extend(self.main_sources) - sources.extend(self.child_sources) - sources.extend(self.source_code_sources) - for source in sources: - # Avoid creating duplicate entries - source.uri = uri - for comp in source.comps: - if [source.uri, source.dist, comp] in seen: - source.comps.remove(comp) - else: - seen.append([source.uri, source.dist, comp]) - if len(source.comps) < 1: - self.sourceslist.remove(source) - - def is_codename(self, name): - ''' Compare a given name with the release codename. ''' - if name == self.codename: - return True - else: - return False - - def get_server_list(self): - ''' Return a list of used and suggested servers ''' - # Store all available servers: - # Name, URI, active - mirrors = [] - - mirrors.append([_("Main server"), self.main_server, - len(self.used_servers) == 1 and - self.used_servers[0] == self.main_server]) - - if len(self.used_servers) == 1 and not re.match(self.used_servers[0], - self.main_server): - # Only one server is used - server = self.used_servers[0] - mirrors.append([server, server, True]) - elif len(self.used_servers) > 1: - # More than one server is used. Since we don't handle this case - # in the user interface we set "custom servers" to true and - # append a list of all used servers - mirrors.append([_("Custom servers"), None, True]) - for server in self.used_servers: - if not [server, server, False] in mirrors: - mirrors.append([server, server, False]) - - return mirrors - - if len(self.used_servers) == 1 and not is_single_server(self.main_server): - mirrors.append(["%s" % self.used_servers[0], - self.used_servers[0], True]) - elif len(self.used_servers) > 1 or not is_single_server(self.main_server): - mirrors.append([_("Custom servers"), None, True]) - - return mirrors - - -class DebianDistribution(Distribution): - ''' Class to support specific Debian features ''' - - def is_codename(self, name): - ''' Compare a given name with the release codename and check if - if it can be used as a synonym for a development releases ''' - if name == self.codename or self.release in ("testing", "unstable"): - return True - else: - return False - -class UbuntuDistribution(Distribution): - ''' Class to support specific Ubuntu features ''' - def get_mirrors(self): - Distribution.get_mirrors(self) - # get a list of country codes and real names - self.countries = {} - try: - f = open("/usr/share/iso-codes/iso_3166.tab", "r") - lines = f.readlines() - for line in lines: - parts = line.split("\t") - self.countries[parts[0].lower()] = parts[1].strip() - except: - print "could not open file '%s'" % file - else: - f.close() - - # try to guess the nearest mirror from the locale - self.country = None - self.country_code = None - locale = os.getenv("LANG", default="en.UK") - a = locale.find("_") - z = locale.find(".") - if z == -1: - z = len(locale) - country_code = locale[a+1:z].lower() - self.nearest_server = "http://%s.archive.ubuntu.com/ubuntu/" % \ - country_code - if self.countries.has_key(country_code): - self.country = self.countries[country_code] - self.country_code = country_code - - def get_server_list(self): - ''' Return a list of used and suggested servers ''' - - def get_mirror_name(server): - ''' Try to get a human readable name for the main mirror of a country''' - country = None - i = server.find("://") - l = server.find(".archive.ubuntu.com") - if i != -1 and l != -1: - country = server[i+len("://"):l] - if self.countries.has_key(country): - # TRANSLATORS: %s is a country - return _("Server for %s") % \ - gettext.dgettext("iso-3166", - self.countries[country].rstrip()).rstrip() - else: - return("%s" % server) - - # Store all available servers: - # Name, URI, active - mirrors = [] - if len(self.used_servers) < 1 or \ - (len(self.used_servers) == 1 and \ - self.used_servers[0] == self.main_server): - mirrors.append([_("Main server"), self.main_server, True]) - mirrors.append([get_mirror_name(self.nearest_server), - self.nearest_server, False]) - elif len(self.used_servers) == 1 and not re.match(self.used_servers[0], - self.main_server): - mirrors.append([_("Main server"), self.main_server, False]) - # Only one server is used - server = self.used_servers[0] - - # Append the nearest server if it's not already used - if not re.match(server, self.nearest_server): - mirrors.append([get_mirror_name(self.nearest_server), - self.nearest_server, False]) - mirrors.append([get_mirror_name(server), server, True]) - - elif len(self.used_servers) > 1: - # More than one server is used. Since we don't handle this case - # in the user interface we set "custom servers" to true and - # append a list of all used servers - mirrors.append([_("Main server"), self.main_server, False]) - mirrors.append([get_mirror_name(self.nearest_server), - self.nearest_server, False]) - mirrors.append([_("Custom servers"), None, True]) - for server in self.used_servers: - if re.match(server, self.nearest_server): - continue - elif not [get_mirror_name(server), server, False] in mirrors: - mirrors.append([get_mirror_name(server), server, False]) - - return mirrors - -def get_distro(): - ''' Check the currently used distribution and return the corresponding - distriubtion class that supports distro specific features. ''' - lsb_info = [] - for lsb_option in ["-i", "-c", "-d", "-r"]: - pipe = os.popen("lsb_release %s -s" % lsb_option) - lsb_info.append(pipe.read().strip()) - del pipe - (id, codename, description, release) = lsb_info - if id == "Ubuntu": - return UbuntuDistribution(id, codename, description, - release) - elif id == "Debian": - return DebianDistribution(id, codename, description, - release) - else: - return Distribution(id, codename, description, relase) # some simple tests if __name__ == "__main__": diff --git a/aptsources/distinfo.py b/aptsources/distinfo.py new file mode 100644 index 00000000..7568b558 --- /dev/null +++ b/aptsources/distinfo.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python +# +# distinfo.py - provide meta information for distro repositories +# +# Copyright (c) 2005 Gustavo Noronha Silva +# 2006-2007 Sebastian Heinlein +# +# Author: Gustavo Noronha Silva +# Sebastian Heinlein +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA + +import os +import gettext +from os import getenv +import ConfigParser +import string + +from gettext import gettext as _ + +import re + +class Template: + def __init__(self): + self.name = None + self.child = False + self.match_name = None + self.description = None + self.base_uri = None + self.type = None + self.components = [] + self.children = [] + self.match_uri = None + self.mirror_set = {} + self.distribution = None + self.available = True + + def has_component(self, comp): + ''' Check if the distribution provides the given component ''' + return comp in map(lambda c: c.name, self.components) + + def is_mirror(self, url): + ''' Check if a given url of a repository is a valid mirror ''' + proto, hostname, dir = split_url(url) + if self.mirror_set.has_key(hostname): + return self.mirror_set[hostname].has_repository(proto, dir) + else: + return False + +class Component: + def __init__(self, name, desc=None, long_desc=None): + self.name = name + self.description = desc + self.description_long = long_desc + def get_description(self): + if self.description_long != None: + return self.description_long + elif self.description != None: + return self.description + else: + return None + def set_description(self, desc): + self.description = desc + def set_description_long(self, desc): + self.description_long = desc + def get_description_long(self): + return self.description_long + +class Mirror: + ''' Storage for mirror related information ''' + def __init__(self, proto, hostname, dir, location=None): + self.hostname = hostname + self.repositories = [] + self.add_repository(proto, dir) + self.location = location + def add_repository(self, proto, dir): + self.repositories.append(Repository(proto, dir)) + def get_repositories_for_proto(self, proto): + return filter(lambda r: r.proto == proto, self.repositories) + def has_repository(self, proto, dir): + return len(filter(lambda r: (r.proto == proto) and dir in r.dir, + self.repositories)) > 0 + def get_repo_urls(self): + return map(lambda r: r.get_url(self.hostname), self.repositories) + def get_location(self): + return self.location + def set_location(self, location): + self.location = location + +class Repository: + def __init__(self, proto, dir): + self.proto = proto + self.dir = dir + def get_info(self): + return self.proto, self.dir + def get_url(self, hostname): + return "%s://%s/%s" % (self.proto, hostname, self.dir) + +def split_url(url): + ''' split a given URL into the protocoll, the hostname and the dir part ''' + return map(lambda a,b: a, re.split(":*\/+", url, maxsplit=2), + [None, None, None]) + +class DistInfo: + def __init__(self, + dist = None, + base_dir = "/usr/share/python-aptsources/templates"): + self.metarelease_uri = '' + self.templates = [] + + location = None + match_loc = re.compile(r"^#LOC:(.+)$") + match_mirror_line = re.compile(r"^(#LOC:.+)|(((http)|(ftp)|(rsync)|(file)|(https))://[A-Za-z/\.:\-_]+)$") + #match_mirror_line = re.compile(r".+") + + if not dist: + pipe = os.popen("lsb_release -i -s") + dist = pipe.read().strip() + pipe.close() + del pipe + + self.dist = dist + + + map_mirror_sets = {} + + dist_fname = "%s/%s.info" % (base_dir, dist) + dist_file = open (dist_fname) + if not dist_file: + return + template = None + component = None + for line in dist_file: + tokens = line.split (':', 1) + if len (tokens) < 2: + continue + field = tokens[0].strip () + value = tokens[1].strip () + if field == 'ChangelogURI': + self.changelogs_uri = _(value) + elif field == 'MetaReleaseURI': + self.metarelease_uri = value + elif field == 'Suite': + if template: + if component and not template.has_component(component.name): + template.components.append(component) + component = None + self.templates.append(template) + template = Template() + template.name = value + template.distribution = dist + template.match_name = "^%s$" % value + elif field == 'MatchName': + template.match_name = value + elif field == 'ParentSuite': + template.child = True + for nanny in self.templates: + if nanny.name == value: + nanny.children.append(template) + # reuse some properties of the parent template + if template.match_uri == None: + template.match_uri = nanny.match_uri + if template.mirror_set == {}: + template.mirror_set = nanny.mirror_set + if template.base_uri == None: + template.base_uri = nanny.base_uri + elif field == 'Available': + template.available = value + elif field == 'RepositoryType': + template.type = value + elif field == 'BaseURI': + template.base_uri = value + template.match_uri = value + elif field == 'MatchURI': + template.match_uri = value + elif field == 'MirrorsFile': + if not map_mirror_sets.has_key(value): + mirror_set = {} + try: + mirror_data = filter(match_mirror_line.match, + map(string.strip, open(value))) + except: + print "ERROR: Failed to read mirror file" + mirrors = [] + for line in mirror_data: + if line.startswith("#LOC:"): + location = match_loc.sub(r"\1", line) + continue + (proto, hostname, dir) = split_url(line) + if mirror_set.has_key(hostname): + mirror_set[hostname].add_repository(proto, dir) + else: + mirror_set[hostname] = Mirror(proto, hostname, dir, location) + map_mirror_sets[value] = mirror_set + template.mirror_set = map_mirror_sets[value] + elif field == 'Description': + template.description = _(value) + elif field == 'Component': + if component and not template.has_component(component.name): + template.components.append(component) + component = Component(value) + elif field == 'CompDescription': + component.set_description(_(value)) + elif field == 'CompDescriptionLong': + component.set_description_long(_(value)) + if template: + if component: + template.components.append(component) + component = None + self.templates.append(template) + template = None + +if __name__ == "__main__": + d = DistInfo ("Ubuntu", "/usr/share/python-apt/templates") + print d.changelogs_uri + for template in d.templates: + print "\nSuite: %s" % template.name + print "Desc: %s" % template.description + print "BaseURI: %s" % template.base_uri + print "MatchURI: %s" % template.match_uri + if template.mirror_set != {}: + print "Mirrors: %s" % template.mirror_set.keys() + for comp in template.components: + print " %s -%s -%s" % (comp.name, + comp.description, + comp.description_long) + for child in template.children: + print " %s" % child.description diff --git a/aptsources/distro.py b/aptsources/distro.py new file mode 100644 index 00000000..18a8ca46 --- /dev/null +++ b/aptsources/distro.py @@ -0,0 +1,422 @@ +# distro.py - Provide a distro abstraction of the sources.list +# +# Copyright (c) 2004-2007 Canonical Ltd. +# 2006-2007 Sebastian Heinlein +# +# Authors: Sebastian Heinlein +# Michael Vogt +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA + +class Distribution: + def __init__(self, id, codename, description, release): + """ Container for distribution specific informations """ + # LSB information + self.id = id + self.codename = codename + self.description = description + self.release = release + + self.binary_type = "deb" + self.source_type = "deb-src" + + def get_sources(self, sourceslist): + """ + Find the corresponding template, main and child sources + for the distribution + """ + + self.sourceslist = sourceslist + # corresponding sources + self.source_template = None + self.child_sources = [] + self.main_sources = [] + self.disabled_sources = [] + self.cdrom_sources = [] + self.download_comps = [] + self.enabled_comps = [] + self.cdrom_comps = [] + self.used_media = [] + self.get_source_code = False + self.source_code_sources = [] + + # location of the sources + self.default_server = "" + self.main_server = "" + self.nearest_server = "" + self.used_servers = [] + + # find the distro template + for template in self.sourceslist.matcher.templates: + if self.is_codename(template.name) and\ + template.distribution == self.id: + #print "yeah! found a template for %s" % self.description + #print template.description, template.base_uri, template.components + self.source_template = template + break + if self.source_template == None: + print "Error: could not find a distribution template" + # FIXME: will go away - only for debugging issues + sys.exit(1) + + # find main and child sources + media = [] + comps = [] + cdrom_comps = [] + enabled_comps = [] + source_code = [] + for source in self.sourceslist.list: + if source.invalid == False and\ + self.is_codename(source.dist) and\ + source.template and\ + self.is_codename(source.template.name): + #print "yeah! found a distro repo: %s" % source.line + # cdroms need do be handled differently + if source.uri.startswith("cdrom:") and \ + source.disabled == False: + self.cdrom_sources.append(source) + cdrom_comps.extend(source.comps) + elif source.uri.startswith("cdrom:") and \ + source.disabled == True: + self.cdrom_sources.append(source) + elif source.type == self.binary_type and \ + source.disabled == False: + self.main_sources.append(source) + comps.extend(source.comps) + media.append(source.uri) + elif source.type == self.binary_type and \ + source.disabled == True: + self.disabled_sources.append(source) + elif source.type == self.source_type and source.disabled == False: + self.source_code_sources.append(source) + elif source.type == self.source_type and source.disabled == True: + self.disabled_sources.append(source) + if source.invalid == False and\ + source.template in self.source_template.children: + if source.disabled == False and source.type == self.binary_type: + self.child_sources.append(source) + elif source.disabled == False and source.type == self.source_type: + self.source_code_sources.append(source) + else: + self.disabled_sources.append(source) + self.download_comps = set(comps) + self.cdrom_comps = set(cdrom_comps) + enabled_comps.extend(comps) + enabled_comps.extend(cdrom_comps) + self.enabled_comps = set(enabled_comps) + self.used_media = set(media) + + self.get_mirrors() + + def get_mirrors(self): + """ + Provide a set of mirrors where you can get the distribution from + """ + # the main server is stored in the template + self.main_server = self.source_template.base_uri + + # other used servers + for medium in self.used_media: + if not medium.startswith("cdrom:"): + # seems to be a network source + self.used_servers.append(medium) + + if len(self.main_sources) == 0: + self.default_server = self.main_server + else: + self.default_server = self.main_sources[0].uri + + def add_source(self, type=None, + uri=None, dist=None, comps=None, comment=""): + """ + Add distribution specific sources + """ + if uri == None: + # FIXME: Add support for the server selector + uri = self.default_server + if dist == None: + dist = self.codename + if comps == None: + comps = list(self.enabled_comps) + if type == None: + type = self.binary_type + new_source = self.sourceslist.add(type, uri, dist, comps, comment) + # if source code is enabled add a deb-src line after the new + # source + if self.get_source_code == True and tpye == self.binary_type: + self.sourceslist.add(self.source_type, uri, dist, comps, comment, + file=new_source.file, + pos=self.sourceslist.list.index(new_source)+1) + + def enable_component(self, comp): + """ + Enable a component in all main, child and source code sources + (excluding cdrom based sources) + + sourceslist: an aptsource.sources_list + comp: the component that should be enabled + """ + def add_component_only_once(source, comps_per_dist): + """ + Check if we already added the component to the repository, since + a repository could be splitted into different apt lines. If not + add the component + """ + # if we don't that distro, just reutnr (can happen for e.g. + # dapper-update only in deb-src + if not comps_per_dist.has_key(source.dist): + return + # if we have seen this component already for this distro, + # return (nothing to do + if comp in comps_per_dist[source.dist]: + return + # add it + source.comps.append(comp) + comps_per_dist[source.dist].add(comp) + + sources = [] + sources.extend(self.main_sources) + sources.extend(self.child_sources) + # store what comps are enabled already per distro (where distro is + # e.g. "dapper", "dapper-updates") + comps_per_dist = {} + comps_per_sdist = {} + for s in sources: + if s.type == self.binary_type: + if not comps_per_dist.has_key(s.dist): + comps_per_dist[s.dist] = set() + map(comps_per_dist[s.dist].add, s.comps) + for s in self.source_code_sources: + if s.type == self.source_type: + if not comps_per_sdist.has_key(s.dist): + comps_per_sdist[s.dist] = set() + map(comps_per_sdist[s.dist].add, s.comps) + + # check if there is a main source at all + if len(self.main_sources) < 1: + # create a new main source + self.add_source(comps=["%s"%comp]) + else: + # add the comp to all main, child and source code sources + for source in sources: + add_component_only_once(source, comps_per_dist) + + # check if there is a main source code source at all + if self.get_source_code == True: + if len(self.source_code_sources) < 1: + # create a new main source + self.add_source(type=self.source_type, comps=["%s"%comp]) + else: + # add the comp to all main, child and source code sources + for source in self.source_code_sources: + add_component_only_once(source, comps_per_sdist) + + def disable_component(self, comp): + """ + Disable a component in all main, child and source code sources + (excluding cdrom based sources) + """ + sources = [] + sources.extend(self.main_sources) + sources.extend(self.child_sources) + sources.extend(self.source_code_sources) + if comp in self.cdrom_comps: + sources = [] + sources.extend(self.main_sources) + + for source in sources: + if comp in source.comps: + source.comps.remove(comp) + if len(source.comps) < 1: + self.sourceslist.remove(source) + + def change_server(self, uri): + ''' Change the server of all distro specific sources to + a given host ''' + sources = [] + seen = [] + self.default_server = uri + sources.extend(self.main_sources) + sources.extend(self.child_sources) + sources.extend(self.source_code_sources) + for source in sources: + # Avoid creating duplicate entries + source.uri = uri + for comp in source.comps: + if [source.uri, source.dist, comp] in seen: + source.comps.remove(comp) + else: + seen.append([source.uri, source.dist, comp]) + if len(source.comps) < 1: + self.sourceslist.remove(source) + + def is_codename(self, name): + ''' Compare a given name with the release codename. ''' + if name == self.codename: + return True + else: + return False + + def get_server_list(self): + ''' Return a list of used and suggested servers ''' + # Store all available servers: + # Name, URI, active + mirrors = [] + + mirrors.append([_("Main server"), self.main_server, + len(self.used_servers) == 1 and + self.used_servers[0] == self.main_server]) + + if len(self.used_servers) == 1 and not re.match(self.used_servers[0], + self.main_server): + # Only one server is used + server = self.used_servers[0] + mirrors.append([server, server, True]) + elif len(self.used_servers) > 1: + # More than one server is used. Since we don't handle this case + # in the user interface we set "custom servers" to true and + # append a list of all used servers + mirrors.append([_("Custom servers"), None, True]) + for server in self.used_servers: + if not [server, server, False] in mirrors: + mirrors.append([server, server, False]) + + return mirrors + + if len(self.used_servers) == 1 and not is_single_server(self.main_server): + mirrors.append(["%s" % self.used_servers[0], + self.used_servers[0], True]) + elif len(self.used_servers) > 1 or not is_single_server(self.main_server): + mirrors.append([_("Custom servers"), None, True]) + + return mirrors + + +class DebianDistribution(Distribution): + ''' Class to support specific Debian features ''' + + def is_codename(self, name): + ''' Compare a given name with the release codename and check if + if it can be used as a synonym for a development releases ''' + if name == self.codename or self.release in ("testing", "unstable"): + return True + else: + return False + +class UbuntuDistribution(Distribution): + ''' Class to support specific Ubuntu features ''' + def get_mirrors(self): + Distribution.get_mirrors(self) + # get a list of country codes and real names + self.countries = {} + try: + f = open("/usr/share/iso-codes/iso_3166.tab", "r") + lines = f.readlines() + for line in lines: + parts = line.split("\t") + self.countries[parts[0].lower()] = parts[1].strip() + except: + print "could not open file '%s'" % file + else: + f.close() + + # try to guess the nearest mirror from the locale + self.country = None + self.country_code = None + locale = os.getenv("LANG", default="en.UK") + a = locale.find("_") + z = locale.find(".") + if z == -1: + z = len(locale) + country_code = locale[a+1:z].lower() + self.nearest_server = "http://%s.archive.ubuntu.com/ubuntu/" % \ + country_code + if self.countries.has_key(country_code): + self.country = self.countries[country_code] + self.country_code = country_code + + def get_server_list(self): + ''' Return a list of used and suggested servers ''' + + def get_mirror_name(server): + ''' Try to get a human readable name for the main mirror of a country''' + country = None + i = server.find("://") + l = server.find(".archive.ubuntu.com") + if i != -1 and l != -1: + country = server[i+len("://"):l] + if self.countries.has_key(country): + # TRANSLATORS: %s is a country + return _("Server for %s") % \ + gettext.dgettext("iso-3166", + self.countries[country].rstrip()).rstrip() + else: + return("%s" % server) + + # Store all available servers: + # Name, URI, active + mirrors = [] + if len(self.used_servers) < 1 or \ + (len(self.used_servers) == 1 and \ + self.used_servers[0] == self.main_server): + mirrors.append([_("Main server"), self.main_server, True]) + mirrors.append([get_mirror_name(self.nearest_server), + self.nearest_server, False]) + elif len(self.used_servers) == 1 and not re.match(self.used_servers[0], + self.main_server): + mirrors.append([_("Main server"), self.main_server, False]) + # Only one server is used + server = self.used_servers[0] + + # Append the nearest server if it's not already used + if not re.match(server, self.nearest_server): + mirrors.append([get_mirror_name(self.nearest_server), + self.nearest_server, False]) + mirrors.append([get_mirror_name(server), server, True]) + + elif len(self.used_servers) > 1: + # More than one server is used. Since we don't handle this case + # in the user interface we set "custom servers" to true and + # append a list of all used servers + mirrors.append([_("Main server"), self.main_server, False]) + mirrors.append([get_mirror_name(self.nearest_server), + self.nearest_server, False]) + mirrors.append([_("Custom servers"), None, True]) + for server in self.used_servers: + if re.match(server, self.nearest_server): + continue + elif not [get_mirror_name(server), server, False] in mirrors: + mirrors.append([get_mirror_name(server), server, False]) + + return mirrors + +def get_distro(): + ''' Check the currently used distribution and return the corresponding + distriubtion class that supports distro specific features. ''' + lsb_info = [] + for lsb_option in ["-i", "-c", "-d", "-r"]: + pipe = os.popen("lsb_release %s -s" % lsb_option) + lsb_info.append(pipe.read().strip()) + del pipe + (id, codename, description, release) = lsb_info + if id == "Ubuntu": + return UbuntuDistribution(id, codename, description, + release) + elif id == "Debian": + return DebianDistribution(id, codename, description, + release) + else: + return Distribution(id, codename, description, relase) -- cgit v1.2.3 From 11dd4cbee2d63932f851a6f9171908951ff08000 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 19 Jan 2007 11:56:04 +0100 Subject: * rename aptsources to sourceslist (aptsources is already used by the package, since the other parts were moved away this file only contains sources.list parsing and manipulating only) * rename get_ubuntu_lp_mirror to get_ubuntu_mirrors_from_lp (should be clearer) * fix some imports --- aptsources/aptsources.py | 428 ------------------------------------ aptsources/distro.py | 8 + aptsources/sourceslist.py | 428 ++++++++++++++++++++++++++++++++++++ utils/get_ubuntu_lp_mirrors.py | 85 ------- utils/get_ubuntu_mirrors_from_lp.py | 85 +++++++ utils/mirrortest | 7 +- 6 files changed, 525 insertions(+), 516 deletions(-) delete mode 100644 aptsources/aptsources.py create mode 100644 aptsources/sourceslist.py delete mode 100755 utils/get_ubuntu_lp_mirrors.py create mode 100755 utils/get_ubuntu_mirrors_from_lp.py (limited to 'aptsources') diff --git a/aptsources/aptsources.py b/aptsources/aptsources.py deleted file mode 100644 index 0928a0e8..00000000 --- a/aptsources/aptsources.py +++ /dev/null @@ -1,428 +0,0 @@ -# aptsource.py - Provide an abstraction of the sources.list -# -# Copyright (c) 2004-2007 Canonical Ltd. -# 2004 Michiel Sikkes -# 2006-2007 Sebastian Heinlein -# -# Author: Michiel Sikkes -# Michael Vogt -# Sebastian Heinlein -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA - -import string -import gettext -import re -import apt_pkg -import glob -import shutil -import time -import os.path -import sys -from gettext import gettext as _ -#import pdb - -#from UpdateManager.Common.DistInfo import DistInfo -from DistInfo import DistInfo - -# some global helpers -def is_mirror(master_uri, compare_uri): - """check if the given add_url is idential or a mirror of orig_uri - e.g. master_uri = archive.ubuntu.com - compare_uri = de.archive.ubuntu.com - -> True - """ - # remove traling spaces and "/" - compare_uri = compare_uri.rstrip("/ ") - master_uri = master_uri.rstrip("/ ") - # uri is identical - if compare_uri == master_uri: - #print "Identical" - return True - # add uri is a master site and orig_uri has the from "XX.mastersite" - # (e.g. de.archive.ubuntu.com) - try: - compare_srv = compare_uri.split("//")[1] - master_srv = master_uri.split("//")[1] - #print "%s == %s " % (add_srv, orig_srv) - except IndexError: # ok, somethings wrong here - #print "IndexError" - return False - # remove the leading "." (if any) and see if that helps - if "." in compare_srv and \ - compare_srv[compare_srv.index(".")+1:] == master_srv: - #print "Mirror" - return True - return False - -def uniq(s): - """ simple and efficient way to return uniq list """ - return list(set(s)) - -class SourceEntry: - """ single sources.list entry """ - def __init__(self, line,file=None): - self.invalid = False # is the source entry valid - self.disabled = False # is it disabled ('#' in front) - self.type = "" # what type (deb, deb-src) - self.uri = "" # base-uri - self.dist = "" # distribution (dapper, edgy, etc) - self.comps = [] # list of available componetns (may empty) - self.comment = "" # (optional) comment - self.line = line # the original sources.list line - if file == None: - file = apt_pkg.Config.FindDir("Dir::Etc")+apt_pkg.Config.Find("Dir::Etc::sourcelist") - self.file = file # the file that the entry is located in - self.parse(line) - self.template = None # type DistInfo.Suite - self.children = [] - - def __eq__(self, other): - """ equal operator for two sources.list entries """ - return (self.disabled == other.disabled and - self.type == other.type and - self.uri == other.uri and - self.dist == other.dist and - self.comps == other.comps) - - - def mysplit(self, line): - """ a split() implementation that understands the sources.list - format better and takes [] into account (for e.g. cdroms) """ - line = string.strip(line) - pieces = [] - tmp = "" - # we are inside a [..] block - p_found = False - space_found = False - for i in range(len(line)): - if line[i] == "[": - p_found=True - tmp += line[i] - elif line[i] == "]": - p_found=False - tmp += line[i] - elif space_found and not line[i].isspace(): # we skip one or more space - space_found = False - pieces.append(tmp) - tmp = line[i] - elif line[i].isspace() and not p_found: # found a whitespace - space_found = True - else: - tmp += line[i] - # append last piece - if len(tmp) > 0: - pieces.append(tmp) - return pieces - - def parse(self,line): - """ parse a given sources.list (textual) line and break it up - into the field we have """ - line = string.strip(self.line) - #print line - # check if the source is enabled/disabled - if line == "" or line == "#": # empty line - self.invalid = True - return - if line[0] == "#": - self.disabled = True - pieces = string.split(line[1:]) - # if it looks not like a disabled deb line return - if not pieces[0] in ("rpm", "rpm-src", "deb", "deb-src"): - self.invalid = True - return - else: - line = line[1:] - # check for another "#" in the line (this is treated as a comment) - i = line.find("#") - if i > 0: - self.comment = line[i+1:] - line = line[:i] - # source is ok, split it and see what we have - pieces = self.mysplit(line) - # Sanity check - if len(pieces) < 3: - self.invalid = True - return - # Type, deb or deb-src - self.type = string.strip(pieces[0]) - # Sanity check - if self.type not in ("deb", "deb-src", "rpm", "rpm-src"): - self.invalid = True - return - # URI - self.uri = string.strip(pieces[1]) - if len(self.uri) < 1: - self.invalid = True - # distro and components (optional) - # Directory or distro - self.dist = string.strip(pieces[2]) - if len(pieces) > 3: - # List of components - self.comps = pieces[3:] - else: - self.comps = [] - - def set_enabled(self, new_value): - """ set a line to enabled or disabled """ - self.disabled = not new_value - # enable, remove all "#" from the start of the line - if new_value == True: - i=0 - self.line = string.lstrip(self.line) - while self.line[i] == "#": - i += 1 - self.line = self.line[i:] - else: - # disabled, add a "#" - if string.strip(self.line)[0] != "#": - self.line = "#" + self.line - - def __str__(self): - """ debug helper """ - return self.str().strip() - - def str(self): - """ return the current line as string """ - if self.invalid: - return self.line - line = "" - if self.disabled: - line = "# " - line += "%s %s %s" % (self.type, self.uri, self.dist) - if len(self.comps) > 0: - line += " " + " ".join(self.comps) - if self.comment != "": - line += " #"+self.comment - line += "\n" - return line - -class NullMatcher(object): - """ a Matcher that does nothing """ - def match(self, s): - return True - -class SourcesList: - """ represents the full sources.list + sources.list.d file """ - def __init__(self, - withMatcher=True, - matcherPath="/usr/share/python-aptsources/templates/"): - self.list = [] # the actual SourceEntries Type - if withMatcher: - self.matcher = SourceEntryMatcher(matcherPath) - else: - self.matcher = NullMatcher() - self.refresh() - - def refresh(self): - """ update the list of known entries """ - self.list = [] - # read sources.list - dir = apt_pkg.Config.FindDir("Dir::Etc") - file = apt_pkg.Config.Find("Dir::Etc::sourcelist") - self.load(dir+file) - # read sources.list.d - partsdir = apt_pkg.Config.FindDir("Dir::Etc::sourceparts") - for file in glob.glob("%s/*.list" % partsdir): - self.load(file) - # check if the source item fits a predefined template - for source in self.list: - if source.invalid == False: - self.matcher.match(source) - - def __iter__(self): - """ simple iterator to go over self.list, returns SourceEntry - types """ - for entry in self.list: - yield entry - raise StopIteration - - def add(self, type, uri, dist, comps, comment="", pos=-1, file=None): - """ - Add a new source to the sources.list. - The method will search for existing matching repos and will try to - reuse them as far as possible - """ - # check if we have this source already in the sources.list - for source in self.list: - if source.disabled == False and source.invalid == False and \ - source.type == type and uri == source.uri and \ - source.dist == dist: - for new_comp in comps: - if new_comp in source.comps: - # we have this component already, delete it from the new_comps - # list - del comps[comps.index(new_comp)] - if len(comps) == 0: - return source - for source in self.list: - # if there is a repo with the same (type, uri, dist) just add the - # components - if source.disabled == False and source.invalid == False and \ - source.type == type and uri == source.uri and \ - source.dist == dist: - comps = uniq(source.comps + comps) - source.comps = comps - return source - # if there is a corresponding repo which is disabled, enable it - elif source.disabled == True and source.invalid == False and \ - source.type == type and uri == source.uri and \ - source.dist == dist and \ - len(set(source.comps) & set(comps)) == len(comps): - source.disabled = False - return source - # there isn't any matching source, so create a new line and parse it - line = "%s %s %s" % (type,uri,dist) - for c in comps: - line = line + " " + c; - if comment != "": - line = "%s #%s\n" %(line,comment) - line = line + "\n" - new_entry = SourceEntry(line) - if file != None: - new_entry.file = file - self.matcher.match(new_entry) - self.list.insert(pos, new_entry) - return new_entry - - def remove(self, source_entry): - """ remove the specified entry from the sources.list """ - self.list.remove(source_entry) - - def restoreBackup(self, backup_ext): - " restore sources.list files based on the backup extension " - dir = apt_pkg.Config.FindDir("Dir::Etc") - file = apt_pkg.Config.Find("Dir::Etc::sourcelist") - if os.path.exists(dir+file+backup_ext) and \ - os.path.exists(dir+file): - shutil.copy(dir+file+backup_ext,dir+file) - # now sources.list.d - partsdir = apt_pkg.Config.FindDir("Dir::Etc::sourceparts") - for file in glob.glob("%s/*.list" % partsdir): - if os.path.exists(file+backup_ext): - shutil.copy(file+backup_ext,file) - - def backup(self, backup_ext=None): - """ make a backup of the current source files, if no backup extension - is given, the current date/time is used (and returned) """ - already_backuped = set() - if backup_ext == None: - backup_ext = time.strftime("%y%m%d.%H%M") - for source in self.list: - if not source.file in already_backuped and os.path.exists(source.file): - shutil.copy(source.file,"%s%s" % (source.file,backup_ext)) - return backup_ext - - def load(self,file): - """ (re)load the current sources """ - try: - f = open(file, "r") - lines = f.readlines() - for line in lines: - source = SourceEntry(line,file) - self.list.append(source) - except: - print "could not open file '%s'" % file - else: - f.close() - - def save(self): - """ save the current sources """ - files = {} - for source in self.list: - if not files.has_key(source.file): - files[source.file]=open(source.file,"w") - files[source.file].write(source.str()) - for f in files: - files[f].close() - - def check_for_relations(self, sources_list): - """get all parent and child channels in the sources list""" - parents = [] - used_child_templates = {} - for source in sources_list: - # try to avoid checking uninterressting sources - if source.template == None: - continue - # set up a dict with all used child templates and corresponding - # source entries - if source.template.child == True: - key = source.template - if not used_child_templates.has_key(key): - used_child_templates[key] = [] - temp = used_child_templates[key] - temp.append(source) - else: - # store each source with children aka. a parent :) - if len(source.template.children) > 0: - parents.append(source) - #print self.used_child_templates - #print self.parents - return (parents, used_child_templates) - -# matcher class to make a source entry look nice -# lots of predefined matchers to make it i18n/gettext friendly -class SourceEntryMatcher: - def __init__(self, matcherPath): - self.templates = [] - # Get the human readable channel and comp names from the channel .infos - spec_files = glob.glob("%s/*.info" % matcherPath) - for f in spec_files: - f = os.path.basename(f) - i = f.find(".info") - f = f[0:i] - dist = DistInfo(f,base_dir=matcherPath) - for suite in dist.suites: - if suite.match_uri != None: - self.templates.append(suite) - return - - def match(self, source): - """Add a matching template to the source""" - _ = gettext.gettext - found = False - for template in self.templates: - if (re.search(template.match_uri, source.uri) and - re.match(template.match_name, source.dist)): - found = True - source.template = template - break - elif (template.is_mirror(source.uri) and - re.match(template.match_name, source.dist)): - found = True - source.template = template - break - return found - - -# some simple tests -if __name__ == "__main__": - apt_pkg.InitConfig() - sources = SourcesList() - - for entry in sources: - print entry.str() - #print entry.uri - - mirror = is_mirror("http://archive.ubuntu.com/ubuntu/", - "http://de.archive.ubuntu.com/ubuntu/") - print "is_mirror(): %s" % mirror - - print is_mirror("http://archive.ubuntu.com/ubuntu", - "http://de.archive.ubuntu.com/ubuntu/") - print is_mirror("http://archive.ubuntu.com/ubuntu/", - "http://de.archive.ubuntu.com/ubuntu") - diff --git a/aptsources/distro.py b/aptsources/distro.py index 18a8ca46..5b4c6522 100644 --- a/aptsources/distro.py +++ b/aptsources/distro.py @@ -21,6 +21,14 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA +import string +import gettext +import re +import os +import sys +from gettext import gettext as _ + + class Distribution: def __init__(self, id, codename, description, release): """ Container for distribution specific informations """ diff --git a/aptsources/sourceslist.py b/aptsources/sourceslist.py new file mode 100644 index 00000000..14c2a0ea --- /dev/null +++ b/aptsources/sourceslist.py @@ -0,0 +1,428 @@ +# aptsource.py - Provide an abstraction of the sources.list +# +# Copyright (c) 2004-2007 Canonical Ltd. +# 2004 Michiel Sikkes +# 2006-2007 Sebastian Heinlein +# +# Author: Michiel Sikkes +# Michael Vogt +# Sebastian Heinlein +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA + +import string +import gettext +import re +import apt_pkg +import glob +import shutil +import time +import os.path +import sys +from gettext import gettext as _ +#import pdb + +#from UpdateManager.Common.DistInfo import DistInfo +from distinfo import DistInfo + +# some global helpers +def is_mirror(master_uri, compare_uri): + """check if the given add_url is idential or a mirror of orig_uri + e.g. master_uri = archive.ubuntu.com + compare_uri = de.archive.ubuntu.com + -> True + """ + # remove traling spaces and "/" + compare_uri = compare_uri.rstrip("/ ") + master_uri = master_uri.rstrip("/ ") + # uri is identical + if compare_uri == master_uri: + #print "Identical" + return True + # add uri is a master site and orig_uri has the from "XX.mastersite" + # (e.g. de.archive.ubuntu.com) + try: + compare_srv = compare_uri.split("//")[1] + master_srv = master_uri.split("//")[1] + #print "%s == %s " % (add_srv, orig_srv) + except IndexError: # ok, somethings wrong here + #print "IndexError" + return False + # remove the leading "." (if any) and see if that helps + if "." in compare_srv and \ + compare_srv[compare_srv.index(".")+1:] == master_srv: + #print "Mirror" + return True + return False + +def uniq(s): + """ simple and efficient way to return uniq list """ + return list(set(s)) + +class SourceEntry: + """ single sources.list entry """ + def __init__(self, line,file=None): + self.invalid = False # is the source entry valid + self.disabled = False # is it disabled ('#' in front) + self.type = "" # what type (deb, deb-src) + self.uri = "" # base-uri + self.dist = "" # distribution (dapper, edgy, etc) + self.comps = [] # list of available componetns (may empty) + self.comment = "" # (optional) comment + self.line = line # the original sources.list line + if file == None: + file = apt_pkg.Config.FindDir("Dir::Etc")+apt_pkg.Config.Find("Dir::Etc::sourcelist") + self.file = file # the file that the entry is located in + self.parse(line) + self.template = None # type DistInfo.Suite + self.children = [] + + def __eq__(self, other): + """ equal operator for two sources.list entries """ + return (self.disabled == other.disabled and + self.type == other.type and + self.uri == other.uri and + self.dist == other.dist and + self.comps == other.comps) + + + def mysplit(self, line): + """ a split() implementation that understands the sources.list + format better and takes [] into account (for e.g. cdroms) """ + line = string.strip(line) + pieces = [] + tmp = "" + # we are inside a [..] block + p_found = False + space_found = False + for i in range(len(line)): + if line[i] == "[": + p_found=True + tmp += line[i] + elif line[i] == "]": + p_found=False + tmp += line[i] + elif space_found and not line[i].isspace(): # we skip one or more space + space_found = False + pieces.append(tmp) + tmp = line[i] + elif line[i].isspace() and not p_found: # found a whitespace + space_found = True + else: + tmp += line[i] + # append last piece + if len(tmp) > 0: + pieces.append(tmp) + return pieces + + def parse(self,line): + """ parse a given sources.list (textual) line and break it up + into the field we have """ + line = string.strip(self.line) + #print line + # check if the source is enabled/disabled + if line == "" or line == "#": # empty line + self.invalid = True + return + if line[0] == "#": + self.disabled = True + pieces = string.split(line[1:]) + # if it looks not like a disabled deb line return + if not pieces[0] in ("rpm", "rpm-src", "deb", "deb-src"): + self.invalid = True + return + else: + line = line[1:] + # check for another "#" in the line (this is treated as a comment) + i = line.find("#") + if i > 0: + self.comment = line[i+1:] + line = line[:i] + # source is ok, split it and see what we have + pieces = self.mysplit(line) + # Sanity check + if len(pieces) < 3: + self.invalid = True + return + # Type, deb or deb-src + self.type = string.strip(pieces[0]) + # Sanity check + if self.type not in ("deb", "deb-src", "rpm", "rpm-src"): + self.invalid = True + return + # URI + self.uri = string.strip(pieces[1]) + if len(self.uri) < 1: + self.invalid = True + # distro and components (optional) + # Directory or distro + self.dist = string.strip(pieces[2]) + if len(pieces) > 3: + # List of components + self.comps = pieces[3:] + else: + self.comps = [] + + def set_enabled(self, new_value): + """ set a line to enabled or disabled """ + self.disabled = not new_value + # enable, remove all "#" from the start of the line + if new_value == True: + i=0 + self.line = string.lstrip(self.line) + while self.line[i] == "#": + i += 1 + self.line = self.line[i:] + else: + # disabled, add a "#" + if string.strip(self.line)[0] != "#": + self.line = "#" + self.line + + def __str__(self): + """ debug helper """ + return self.str().strip() + + def str(self): + """ return the current line as string """ + if self.invalid: + return self.line + line = "" + if self.disabled: + line = "# " + line += "%s %s %s" % (self.type, self.uri, self.dist) + if len(self.comps) > 0: + line += " " + " ".join(self.comps) + if self.comment != "": + line += " #"+self.comment + line += "\n" + return line + +class NullMatcher(object): + """ a Matcher that does nothing """ + def match(self, s): + return True + +class SourcesList: + """ represents the full sources.list + sources.list.d file """ + def __init__(self, + withMatcher=True, + matcherPath="/usr/share/python-aptsources/templates/"): + self.list = [] # the actual SourceEntries Type + if withMatcher: + self.matcher = SourceEntryMatcher(matcherPath) + else: + self.matcher = NullMatcher() + self.refresh() + + def refresh(self): + """ update the list of known entries """ + self.list = [] + # read sources.list + dir = apt_pkg.Config.FindDir("Dir::Etc") + file = apt_pkg.Config.Find("Dir::Etc::sourcelist") + self.load(dir+file) + # read sources.list.d + partsdir = apt_pkg.Config.FindDir("Dir::Etc::sourceparts") + for file in glob.glob("%s/*.list" % partsdir): + self.load(file) + # check if the source item fits a predefined template + for source in self.list: + if source.invalid == False: + self.matcher.match(source) + + def __iter__(self): + """ simple iterator to go over self.list, returns SourceEntry + types """ + for entry in self.list: + yield entry + raise StopIteration + + def add(self, type, uri, dist, comps, comment="", pos=-1, file=None): + """ + Add a new source to the sources.list. + The method will search for existing matching repos and will try to + reuse them as far as possible + """ + # check if we have this source already in the sources.list + for source in self.list: + if source.disabled == False and source.invalid == False and \ + source.type == type and uri == source.uri and \ + source.dist == dist: + for new_comp in comps: + if new_comp in source.comps: + # we have this component already, delete it from the new_comps + # list + del comps[comps.index(new_comp)] + if len(comps) == 0: + return source + for source in self.list: + # if there is a repo with the same (type, uri, dist) just add the + # components + if source.disabled == False and source.invalid == False and \ + source.type == type and uri == source.uri and \ + source.dist == dist: + comps = uniq(source.comps + comps) + source.comps = comps + return source + # if there is a corresponding repo which is disabled, enable it + elif source.disabled == True and source.invalid == False and \ + source.type == type and uri == source.uri and \ + source.dist == dist and \ + len(set(source.comps) & set(comps)) == len(comps): + source.disabled = False + return source + # there isn't any matching source, so create a new line and parse it + line = "%s %s %s" % (type,uri,dist) + for c in comps: + line = line + " " + c; + if comment != "": + line = "%s #%s\n" %(line,comment) + line = line + "\n" + new_entry = SourceEntry(line) + if file != None: + new_entry.file = file + self.matcher.match(new_entry) + self.list.insert(pos, new_entry) + return new_entry + + def remove(self, source_entry): + """ remove the specified entry from the sources.list """ + self.list.remove(source_entry) + + def restoreBackup(self, backup_ext): + " restore sources.list files based on the backup extension " + dir = apt_pkg.Config.FindDir("Dir::Etc") + file = apt_pkg.Config.Find("Dir::Etc::sourcelist") + if os.path.exists(dir+file+backup_ext) and \ + os.path.exists(dir+file): + shutil.copy(dir+file+backup_ext,dir+file) + # now sources.list.d + partsdir = apt_pkg.Config.FindDir("Dir::Etc::sourceparts") + for file in glob.glob("%s/*.list" % partsdir): + if os.path.exists(file+backup_ext): + shutil.copy(file+backup_ext,file) + + def backup(self, backup_ext=None): + """ make a backup of the current source files, if no backup extension + is given, the current date/time is used (and returned) """ + already_backuped = set() + if backup_ext == None: + backup_ext = time.strftime("%y%m%d.%H%M") + for source in self.list: + if not source.file in already_backuped and os.path.exists(source.file): + shutil.copy(source.file,"%s%s" % (source.file,backup_ext)) + return backup_ext + + def load(self,file): + """ (re)load the current sources """ + try: + f = open(file, "r") + lines = f.readlines() + for line in lines: + source = SourceEntry(line,file) + self.list.append(source) + except: + print "could not open file '%s'" % file + else: + f.close() + + def save(self): + """ save the current sources """ + files = {} + for source in self.list: + if not files.has_key(source.file): + files[source.file]=open(source.file,"w") + files[source.file].write(source.str()) + for f in files: + files[f].close() + + def check_for_relations(self, sources_list): + """get all parent and child channels in the sources list""" + parents = [] + used_child_templates = {} + for source in sources_list: + # try to avoid checking uninterressting sources + if source.template == None: + continue + # set up a dict with all used child templates and corresponding + # source entries + if source.template.child == True: + key = source.template + if not used_child_templates.has_key(key): + used_child_templates[key] = [] + temp = used_child_templates[key] + temp.append(source) + else: + # store each source with children aka. a parent :) + if len(source.template.children) > 0: + parents.append(source) + #print self.used_child_templates + #print self.parents + return (parents, used_child_templates) + +# matcher class to make a source entry look nice +# lots of predefined matchers to make it i18n/gettext friendly +class SourceEntryMatcher: + def __init__(self, matcherPath): + self.templates = [] + # Get the human readable channel and comp names from the channel .infos + spec_files = glob.glob("%s/*.info" % matcherPath) + for f in spec_files: + f = os.path.basename(f) + i = f.find(".info") + f = f[0:i] + dist = DistInfo(f,base_dir=matcherPath) + for template in dist.templates: + if template.match_uri != None: + self.templates.append(template) + return + + def match(self, source): + """Add a matching template to the source""" + _ = gettext.gettext + found = False + for template in self.templates: + if (re.search(template.match_uri, source.uri) and + re.match(template.match_name, source.dist)): + found = True + source.template = template + break + elif (template.is_mirror(source.uri) and + re.match(template.match_name, source.dist)): + found = True + source.template = template + break + return found + + +# some simple tests +if __name__ == "__main__": + apt_pkg.InitConfig() + sources = SourcesList() + + for entry in sources: + print entry.str() + #print entry.uri + + mirror = is_mirror("http://archive.ubuntu.com/ubuntu/", + "http://de.archive.ubuntu.com/ubuntu/") + print "is_mirror(): %s" % mirror + + print is_mirror("http://archive.ubuntu.com/ubuntu", + "http://de.archive.ubuntu.com/ubuntu/") + print is_mirror("http://archive.ubuntu.com/ubuntu/", + "http://de.archive.ubuntu.com/ubuntu") + diff --git a/utils/get_ubuntu_lp_mirrors.py b/utils/get_ubuntu_lp_mirrors.py deleted file mode 100755 index fad495b5..00000000 --- a/utils/get_ubuntu_lp_mirrors.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python -# -# get_ubuntu_lp_mirrors.py -# -# Download the latest list with available Ubuntu mirrors from Launchpad.net -# and extract the hosts from the raw page -# -# Copyright (c) 2006 Free Software Foundation Europe -# -# Author: Sebastian Heinlein -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 -# USA - -import urllib2 -import re -import sys - -# the list of official Ubuntu servers -mirrors = [] -# path to the local mirror list -list_path = "../data/templates/Ubuntu.mirrors" - - -try: - f = open("/usr/share/iso-codes/iso_3166.tab", "r") - lines = f.readlines() - f.close() -except: - print "Could not read country information" - sys.exit(1) - -countries = {} -for line in lines: - parts = line.split("\t") - countries[parts[1].strip()] = parts[0].lower() - -req = urllib2.Request("https://launchpad.net/ubuntu/+archivemirrors") -print "Downloading mirrors list from the Ubuntu wiki..." -try: - uri=urllib2.urlopen(req) - content = uri.read() - uri.close() -except: - print "Failed to download or extract the mirrors list!" - sys.exit(1) - -content = content.replace("\n", "") - -content_splits = re.split(r'.+?', - content)[0]) -lines=[] -def find(split): - country = re.search(r"(.+?)", split) - if not country: - return - urls = re.findall(r')(.+?)">', - split) - if countries.has_key(country.group(1)): - lines.append("#LOC:%s" % countries[country.group(1)].upper()) - else: - lines.append("#LOC:%s" % country.group(1)) - map(lines.append, urls) - -map(find, content_splits) - -print "Writing local mirrors list: %s" % list_path -list = open(list_path, "w") -for line in lines: - list.write("%s\n" % line) -list.close() -print "Done." diff --git a/utils/get_ubuntu_mirrors_from_lp.py b/utils/get_ubuntu_mirrors_from_lp.py new file mode 100755 index 00000000..fad495b5 --- /dev/null +++ b/utils/get_ubuntu_mirrors_from_lp.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# +# get_ubuntu_lp_mirrors.py +# +# Download the latest list with available Ubuntu mirrors from Launchpad.net +# and extract the hosts from the raw page +# +# Copyright (c) 2006 Free Software Foundation Europe +# +# Author: Sebastian Heinlein +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA + +import urllib2 +import re +import sys + +# the list of official Ubuntu servers +mirrors = [] +# path to the local mirror list +list_path = "../data/templates/Ubuntu.mirrors" + + +try: + f = open("/usr/share/iso-codes/iso_3166.tab", "r") + lines = f.readlines() + f.close() +except: + print "Could not read country information" + sys.exit(1) + +countries = {} +for line in lines: + parts = line.split("\t") + countries[parts[1].strip()] = parts[0].lower() + +req = urllib2.Request("https://launchpad.net/ubuntu/+archivemirrors") +print "Downloading mirrors list from the Ubuntu wiki..." +try: + uri=urllib2.urlopen(req) + content = uri.read() + uri.close() +except: + print "Failed to download or extract the mirrors list!" + sys.exit(1) + +content = content.replace("\n", "") + +content_splits = re.split(r'.+?', + content)[0]) +lines=[] +def find(split): + country = re.search(r"(.+?)", split) + if not country: + return + urls = re.findall(r')(.+?)">', + split) + if countries.has_key(country.group(1)): + lines.append("#LOC:%s" % countries[country.group(1)].upper()) + else: + lines.append("#LOC:%s" % country.group(1)) + map(lines.append, urls) + +map(find, content_splits) + +print "Writing local mirrors list: %s" % list_path +list = open(list_path, "w") +for line in lines: + list.write("%s\n" % line) +list.close() +print "Done." diff --git a/utils/mirrortest b/utils/mirrortest index 9748c138..75ef917b 100755 --- a/utils/mirrortest +++ b/utils/mirrortest @@ -1,6 +1,7 @@ #!/usr/bin/env python import threading, Queue, time, re, os, tempfile -import AptSources.aptsources as aptsources +import aptsources.sourceslist +import aptsources.distro from timeit import Timer import urllib import socket @@ -87,8 +88,8 @@ class MirrorTest: print "%s: %s KByte/s" % (f[1].hostname, int(f[0])) if __name__ == "__main__": - distro = aptsources.get_distro() - distro.get_sources(aptsources.SourcesList()) + distro = aptsources.distro.get_distro() + distro.get_sources(aptsources.sourceslist.SourcesList()) pipe = os.popen("dpkg --print-architecture") arch = pipe.read().strip() test_file = "dists/%s/%s/binary-%s/Packages.gz" % \ -- cgit v1.2.3 From 020a3e602fa07858410cbbaa81fab3ed4a03e20d Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 2 Feb 2007 20:01:39 +0100 Subject: * change all obsolete references to the former python-aptsources --- aptsources/distinfo.py | 2 +- aptsources/sourceslist.py | 2 +- data/templates/Debian.info.in | 12 ++++++------ data/templates/Ubuntu.info.in | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) (limited to 'aptsources') diff --git a/aptsources/distinfo.py b/aptsources/distinfo.py index 7568b558..d7289b4b 100644 --- a/aptsources/distinfo.py +++ b/aptsources/distinfo.py @@ -117,7 +117,7 @@ def split_url(url): class DistInfo: def __init__(self, dist = None, - base_dir = "/usr/share/python-aptsources/templates"): + base_dir = "/usr/share/python-apt/templates"): self.metarelease_uri = '' self.templates = [] diff --git a/aptsources/sourceslist.py b/aptsources/sourceslist.py index 14c2a0ea..800f462f 100644 --- a/aptsources/sourceslist.py +++ b/aptsources/sourceslist.py @@ -219,7 +219,7 @@ class SourcesList: """ represents the full sources.list + sources.list.d file """ def __init__(self, withMatcher=True, - matcherPath="/usr/share/python-aptsources/templates/"): + matcherPath="/usr/share/python-apt/templates/"): self.list = [] # the actual SourceEntries Type if withMatcher: self.matcher = SourceEntryMatcher(matcherPath) diff --git a/data/templates/Debian.info.in b/data/templates/Debian.info.in index 8a2eead3..244e1c6b 100644 --- a/data/templates/Debian.info.in +++ b/data/templates/Debian.info.in @@ -4,7 +4,7 @@ Suite: etch RepositoryType: deb BaseURI: http://http.us.debian.org/debian/ MatchUri: ftp[0-9]*\.[a-z]\.debian\.org -MirrorsFile: /usr/share/python-aptsources/templates/Debian.mirrors +MirrorsFile: /usr/share/python-apt/templates/Debian.mirrors _Description: Debian 4.0 'Etch' Component: main _CompDescription: Officially supported @@ -27,7 +27,7 @@ Suite: sarge RepositoryType: deb BaseURI: http://http.us.debian.org/debian/ MatchUri: ftp[0-9]*\.[a-z]\.debian\.org -MirrorsFile: /usr/share/python-aptsources/templates/Debian.mirrors +MirrorsFile: /usr/share/python-apt/templates/Debian.mirrors _Description: Debian 3.1 'Sarge' Component: main _CompDescription: Officially supported @@ -50,7 +50,7 @@ Suite: stable RepositoryType: deb BaseURI: http://http.us.debian.org/debian/ MatchUri: ftp[0-9]*\.[a-z]\.debian\.org -MirrorsFile: /usr/share/python-aptsources/templates/Debian.mirrors +MirrorsFile: /usr/share/python-apt/templates/Debian.mirrors _Description: Debian current stable release Component: main _CompDescription: Officially supported @@ -63,7 +63,7 @@ Suite: testing RepositoryType: deb BaseURI: http://http.us.debian.org/debian/ MatchUri: ftp[0-9]*\.[a-z]\.debian\.org -MirrorsFile: /usr/share/python-aptsources/templates/Debian.mirrors +MirrorsFile: /usr/share/python-apt/templates/Debian.mirrors _Description: Debian testing Component: main _CompDescription: Officially supported @@ -76,7 +76,7 @@ Suite: sid RepositoryType: deb BaseURI: http://http.us.debian.org/debian/ MatchUri: ftp[0-9]*\.[a-z]\.debian\.org -MirrorsFile: /usr/share/python-aptsources/templates/Debian.mirrors +MirrorsFile: /usr/share/python-apt/templates/Debian.mirrors _Description: Debian 'Sid' (unstable) Component: main _CompDescription: Officially supported @@ -88,7 +88,7 @@ _CompDescription: Non-DFSG-compatible Software Suite: unstable RepositoryType: deb BaseURI: http://http.us.debian.org/debian/ -MirrorsFile: /usr/share/python-aptsources/templates/Debian.mirrors +MirrorsFile: /usr/share/python-apt/templates/Debian.mirrors _Description: Debian 'Sid' (unstable) Component: main _CompDescription: Officially supported diff --git a/data/templates/Ubuntu.info.in b/data/templates/Ubuntu.info.in index fa76879f..56a02436 100644 --- a/data/templates/Ubuntu.info.in +++ b/data/templates/Ubuntu.info.in @@ -4,7 +4,7 @@ Suite: feisty RepositoryType: deb BaseURI: http://archive.ubuntu.com/ubuntu/ MatchURI: archive.ubuntu.com/ubuntu -MirrorsFile: /usr/share/python-aptsources/templates/Ubuntu.mirrors +MirrorsFile: /usr/share/python-apt/templates/Ubuntu.mirrors _Description: Ubuntu 7.04 'Feisty Fawn' Component: main _CompDescription: Officially supported @@ -55,7 +55,7 @@ Suite: edgy RepositoryType: deb BaseURI: http://archive.ubuntu.com/ubuntu/ MatchURI: archive.ubuntu.com/ubuntu -MirrorsFile: /usr/share/python-aptsources/templates/Ubuntu.mirrors +MirrorsFile: /usr/share/python-apt/templates/Ubuntu.mirrors _Description: Ubuntu 6.10 'Edgy Eft' Component: main _CompDescription: Officially supported @@ -106,7 +106,7 @@ Suite: dapper RepositoryType: deb BaseURI: http://archive.ubuntu.com/ubuntu/ MatchURI: archive.ubuntu.com/ubuntu -MirrorsFile: /usr/share/python-aptsources/templates/Ubuntu.mirrors +MirrorsFile: /usr/share/python-apt/templates/Ubuntu.mirrors _Description: Ubuntu 6.06 LTS 'Dapper Drake' Component: main _CompDescription: Officially supported @@ -157,7 +157,7 @@ Suite: breezy RepositoryType: deb BaseURI: http://archive.ubuntu.com/ubuntu/ MatchURI: archive.ubuntu.com/ubuntu -MirrorsFile: /usr/share/python-aptsources/templates/Ubuntu.mirrors +MirrorsFile: /usr/share/python-apt/templates/Ubuntu.mirrors _Description: Ubuntu 5.10 'Breezy Badger' Component: main _CompDescription: Officially supported @@ -199,7 +199,7 @@ Suite: hoary RepositoryType: deb BaseURI: http://archive.ubuntu.com/ubuntu/ MatchURI: archive.ubuntu.com/ubuntu -MirrorsFile: /usr/share/python-aptsources/templates/Ubuntu.mirrors +MirrorsFile: /usr/share/python-apt/templates/Ubuntu.mirrors _Description: Ubuntu 5.04 'Hoary Hedgehog' Component: main _CompDescription: Officially supported -- cgit v1.2.3 From 3ee70c61924e65e3c4ef6bf0fa0673d2e866b042 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Mon, 5 Feb 2007 09:19:39 +0100 Subject: * write an empty default sources.list if all sources have been removed --- aptsources/distro.py | 1 - aptsources/sourceslist.py | 9 +++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'aptsources') diff --git a/aptsources/distro.py b/aptsources/distro.py index 5b4c6522..f53783dc 100644 --- a/aptsources/distro.py +++ b/aptsources/distro.py @@ -244,7 +244,6 @@ class Distribution: if comp in self.cdrom_comps: sources = [] sources.extend(self.main_sources) - for source in sources: if comp in source.comps: source.comps.remove(comp) diff --git a/aptsources/sourceslist.py b/aptsources/sourceslist.py index 14c2a0ea..e5b8dab9 100644 --- a/aptsources/sourceslist.py +++ b/aptsources/sourceslist.py @@ -342,6 +342,15 @@ class SourcesList: def save(self): """ save the current sources """ files = {} + # write an empty default config file if there aren't any sources + if len(self.list) == 0: + path = "%s%s" % (apt_pkg.Config.FindDir("Dir::Etc"), + apt_pkg.Config.Find("Dir::Etc::sourcelist")) + header = ("## See sources.list(5) for more information, especialy\n" + "# Remember that you can only use http, ftp or file URIs\n" + "# CDROMs are managed through the apt-cdrom tool.\n") + open(path,"w").write(header) + return for source in self.list: if not files.has_key(source.file): files[source.file]=open(source.file,"w") -- cgit v1.2.3 From 9cab3c32bb04b8c87ec73f21566f3d25f5d986d9 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Mon, 5 Feb 2007 13:19:38 +0100 Subject: * aptsources/distinfo.py: - do not crash if the mirror file can not be read --- aptsources/distinfo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'aptsources') diff --git a/aptsources/distinfo.py b/aptsources/distinfo.py index d7289b4b..f3c68cae 100644 --- a/aptsources/distinfo.py +++ b/aptsources/distinfo.py @@ -193,8 +193,8 @@ class DistInfo: mirror_data = filter(match_mirror_line.match, map(string.strip, open(value))) except: - print "ERROR: Failed to read mirror file" - mirrors = [] + print "WARNING: Failed to read mirror file" + mirror_data = [] for line in mirror_data: if line.startswith("#LOC:"): location = match_loc.sub(r"\1", line) -- cgit v1.2.3 From 4d7d86374effe9bfd3a360aaaa7b09758f5c948e Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Tue, 13 Feb 2007 18:36:57 +0100 Subject: * fixed crash in Mirror.has_repository * fixed docstring --- aptsources/distinfo.py | 8 ++++++-- aptsources/distro.py | 2 +- debian/changelog | 12 ++++++++++++ debian/control | 2 +- 4 files changed, 20 insertions(+), 4 deletions(-) (limited to 'aptsources') diff --git a/aptsources/distinfo.py b/aptsources/distinfo.py index f3c68cae..261243b4 100644 --- a/aptsources/distinfo.py +++ b/aptsources/distinfo.py @@ -91,8 +91,12 @@ class Mirror: def get_repositories_for_proto(self, proto): return filter(lambda r: r.proto == proto, self.repositories) def has_repository(self, proto, dir): - return len(filter(lambda r: (r.proto == proto) and dir in r.dir, - self.repositories)) > 0 + if dir is None: + return False + for r in self.repositories: + if r.proto == proto and dir in r.dir: + return True + return False def get_repo_urls(self): return map(lambda r: r.get_url(self.hostname), self.repositories) def get_location(self): diff --git a/aptsources/distro.py b/aptsources/distro.py index f53783dc..9643524b 100644 --- a/aptsources/distro.py +++ b/aptsources/distro.py @@ -174,7 +174,6 @@ class Distribution: Enable a component in all main, child and source code sources (excluding cdrom based sources) - sourceslist: an aptsource.sources_list comp: the component that should be enabled """ def add_component_only_once(source, comps_per_dist): @@ -427,3 +426,4 @@ def get_distro(): release) else: return Distribution(id, codename, description, relase) + diff --git a/debian/changelog b/debian/changelog index 9380ddbb..e5a5b660 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,15 @@ +python-apt (0.6.20ubuntu5) feisty; urgency=low + + * be more robust in has_repository (LP#84897) + + -- Michael Vogt Tue, 13 Feb 2007 17:49:55 +0100 + +python-apt (0.6.20ubuntu4) feisty; urgency=low + + * rebuild against latest libapt + + -- Michael Vogt Tue, 6 Feb 2007 16:40:37 +0100 + python-apt (0.6.20ubuntu3) feisty; urgency=low * fixes in the new 'aptsources' module diff --git a/debian/control b/debian/control index 9e304c29..bf1ca1de 100644 --- a/debian/control +++ b/debian/control @@ -5,7 +5,7 @@ Maintainer: APT Development Team Uploaders: Matt Zimmerman , Michael Vogt Standards-Version: 3.7.2 XS-Python-Version: all -Build-Depends: debhelper (>= 5.0.37.1), libapt-pkg-dev (>= 0.6.45), apt-utils, python-all-dev, python-distutils-extra, cdbs, python-central (>= 0.5) +Build-Depends: debhelper (>= 5.0.37.1), libapt-pkg-dev (>= 0.6.46.4ubuntu7), apt-utils, python-all-dev, python-distutils-extra, cdbs, python-central (>= 0.5) Package: python-apt Architecture: any -- cgit v1.2.3 From d8f0eef84bc05f6e87ebf50bd8780accec03eacb Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Mon, 19 Feb 2007 12:17:58 +0100 Subject: * aptsources/distro.py: - fixed crash in add_source() --- aptsources/distro.py | 2 +- debian/changelog | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'aptsources') diff --git a/aptsources/distro.py b/aptsources/distro.py index 9643524b..7acd1942 100644 --- a/aptsources/distro.py +++ b/aptsources/distro.py @@ -164,7 +164,7 @@ class Distribution: new_source = self.sourceslist.add(type, uri, dist, comps, comment) # if source code is enabled add a deb-src line after the new # source - if self.get_source_code == True and tpye == self.binary_type: + if self.get_source_code == True and type == self.binary_type: self.sourceslist.add(self.source_type, uri, dist, comps, comment, file=new_source.file, pos=self.sourceslist.list.index(new_source)+1) diff --git a/debian/changelog b/debian/changelog index e5a5b660..cb818eea 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +python-apt (0.6.20ubuntu6) feisty; urgency=low + + * aptsources/distro.py: + - fix crash in add_source (LP#85806) + + -- + python-apt (0.6.20ubuntu5) feisty; urgency=low * be more robust in has_repository (LP#84897) -- cgit v1.2.3 From 89ac70a4cdc9507e6db3921497680aea95b6a7c2 Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Mon, 26 Feb 2007 20:13:42 +0100 Subject: * separate the checking of duplicates of binary and source repositories when changing the server of the distro - fix LP #86219 --- aptsources/distro.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'aptsources') diff --git a/aptsources/distro.py b/aptsources/distro.py index f53783dc..ac1b53e7 100644 --- a/aptsources/distro.py +++ b/aptsources/distro.py @@ -253,13 +253,7 @@ class Distribution: def change_server(self, uri): ''' Change the server of all distro specific sources to a given host ''' - sources = [] - seen = [] - self.default_server = uri - sources.extend(self.main_sources) - sources.extend(self.child_sources) - sources.extend(self.source_code_sources) - for source in sources: + def change_server_of_source(source, uri, seen): # Avoid creating duplicate entries source.uri = uri for comp in source.comps: @@ -269,6 +263,16 @@ class Distribution: seen.append([source.uri, source.dist, comp]) if len(source.comps) < 1: self.sourceslist.remove(source) + seen_binary = [] + seen_source = [] + self.default_server = uri + sources = [] + sources.extend(self.main_sources) + sources.extend(self.child_sources) + for source in sources: + change_server_of_source(source, uri, seen_binary) + for source in self.source_code_sources: + change_server_of_source(source, uri, seen_source) def is_codename(self, name): ''' Compare a given name with the release codename. ''' @@ -276,13 +280,13 @@ class Distribution: return True else: return False - + def get_server_list(self): ''' Return a list of used and suggested servers ''' # Store all available servers: # Name, URI, active mirrors = [] - + mirrors.append([_("Main server"), self.main_server, len(self.used_servers) == 1 and self.used_servers[0] == self.main_server]) -- cgit v1.2.3 From 0b8033d96e6144f8bc05583be0b2d1743f3c73a6 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 15 Mar 2007 13:09:32 +0100 Subject: * aptsources/distro.py: - fix typo (LP#84009) --- aptsources/distro.py | 5 ++--- debian/changelog | 7 +++++++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'aptsources') diff --git a/aptsources/distro.py b/aptsources/distro.py index a95fecdd..16fb0dc7 100644 --- a/aptsources/distro.py +++ b/aptsources/distro.py @@ -426,8 +426,7 @@ def get_distro(): return UbuntuDistribution(id, codename, description, release) elif id == "Debian": - return DebianDistribution(id, codename, description, - release) + return DebianDistribution(id, codename, description, release) else: - return Distribution(id, codename, description, relase) + return Distribution(id, codename, description, release) diff --git a/debian/changelog b/debian/changelog index 7e897122..05efcd08 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +python-apt (0.6.20ubuntu14) feisty; urgency=low + + * aptsources/distro.py: + - fix typo (LP#84009) + + -- + python-apt (0.6.20ubuntu13) feisty; urgency=low * fix in the duplicated source checking (thanks to Sebastian Heinlein) -- cgit v1.2.3 From 6bc3d1c5dd643fd3fd29ec2319cbdef6fe721d25 Mon Sep 17 00:00:00 2001 From: Michael Vogt Date: Thu, 22 Mar 2007 16:01:02 +0100 Subject: * use the correct gettext domain --- apt/package.py | 4 +++- aptsources/distinfo.py | 4 +++- aptsources/distro.py | 4 +++- aptsources/sourceslist.py | 5 ++++- debian/changelog | 6 ++++++ 5 files changed, 19 insertions(+), 4 deletions(-) (limited to 'aptsources') diff --git a/apt/package.py b/apt/package.py index 1f373ad3..f7a1a8ac 100644 --- a/apt/package.py +++ b/apt/package.py @@ -24,7 +24,9 @@ import sys import random import string -from gettext import gettext as _ +#from gettext import gettext as _ +import gettext +def _(s): return gettext.dgettext("python-apt", s) class BaseDependency(object): " a single dependency " diff --git a/aptsources/distinfo.py b/aptsources/distinfo.py index 261243b4..674c1bf5 100644 --- a/aptsources/distinfo.py +++ b/aptsources/distinfo.py @@ -29,7 +29,9 @@ from os import getenv import ConfigParser import string -from gettext import gettext as _ +#from gettext import gettext as _ +import gettext +def _(s): return gettext.dgettext("python-apt", s) import re diff --git a/aptsources/distro.py b/aptsources/distro.py index 16fb0dc7..fe9b4d75 100644 --- a/aptsources/distro.py +++ b/aptsources/distro.py @@ -26,7 +26,9 @@ import gettext import re import os import sys -from gettext import gettext as _ +#from gettext import gettext as _ +import gettext +def _(s): return gettext.dgettext("python-apt", s) class Distribution: diff --git a/aptsources/sourceslist.py b/aptsources/sourceslist.py index 208e6c7d..754e2003 100644 --- a/aptsources/sourceslist.py +++ b/aptsources/sourceslist.py @@ -32,9 +32,12 @@ import shutil import time import os.path import sys -from gettext import gettext as _ +#from gettext import gettext as _ #import pdb +import gettext +def _(s): return gettext.dgettext("python-apt", s) + #from UpdateManager.Common.DistInfo import DistInfo from distinfo import DistInfo diff --git a/debian/changelog b/debian/changelog index 760812e1..280fc0cd 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +python-apt (0.6.20ubuntu15) feisty; urgency=low + + * use the correct gettext domain + + -- Michael Vogt Thu, 22 Mar 2007 15:55:54 +0100 + python-apt (0.6.20ubuntu14) feisty; urgency=low * aptsources/distro.py: -- cgit v1.2.3 From ed893f0c1b8978acf6b986eb93a2af15d267319d Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 23 Mar 2007 15:19:02 +0100 Subject: * add a mirror comparision function to clean up the code * handle multiple server in a more sane way --- aptsources/distro.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'aptsources') diff --git a/aptsources/distro.py b/aptsources/distro.py index 1fb499a0..5630259f 100644 --- a/aptsources/distro.py +++ b/aptsources/distro.py @@ -327,7 +327,10 @@ class UbuntuDistribution(Distribution): def get_server_list(self): ''' Return a list of used and suggested servers ''' - + def compare_mirrors(mir1, mir2): + '''Helper function that handles comaprision of mirror urls + that could contain trailing slashes''' + return re.match(mir1.strip("/ "), mir2.rstrip("/ ")) def get_mirror_name(server): ''' Try to get a human readable name for the main mirror of a country''' country = None @@ -341,25 +344,27 @@ class UbuntuDistribution(Distribution): gettext.dgettext("iso-3166", self.countries[country].rstrip()).rstrip() else: - return("%s" % server) + return("%s" % server.rstrip("/ ")) # Store all available servers: # Name, URI, active mirrors = [] + import pdb + pdb.set_trace() if len(self.used_servers) < 1 or \ (len(self.used_servers) == 1 and \ - self.used_servers[0].rstrip("/") == self.main_server.rstrip("/")): + compare_mirrors(self.used_servers[0], self.main_server)): mirrors.append([_("Main server"), self.main_server, True]) mirrors.append([get_mirror_name(self.nearest_server), self.nearest_server, False]) - elif len(self.used_servers) == 1 and not re.match(self.used_servers[0], - self.main_server): + elif len(self.used_servers) == 1 and not \ + compare_mirrors(self.used_servers[0], self.main_server): mirrors.append([_("Main server"), self.main_server, False]) # Only one server is used server = self.used_servers[0] # Append the nearest server if it's not already used - if not re.match(server, self.nearest_server): + if not compare_mirrors(server, self.nearest_server): mirrors.append([get_mirror_name(self.nearest_server), self.nearest_server, False]) mirrors.append([get_mirror_name(server), server, True]) @@ -373,7 +378,8 @@ class UbuntuDistribution(Distribution): self.nearest_server, False]) mirrors.append([_("Custom servers"), None, True]) for server in self.used_servers: - if re.match(server, self.nearest_server): + if compare_mirrors(server, self.nearest_server) or\ + compare_mirrors(server, self.main_server): continue elif not [get_mirror_name(server), server, False] in mirrors: mirrors.append([get_mirror_name(server), server, False]) -- cgit v1.2.3 From bf5f2b17ba2dbb17af7e88b824482e9d9161d47d Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Fri, 23 Mar 2007 15:20:48 +0100 Subject: * missed a debug breakpoint --- aptsources/distro.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'aptsources') diff --git a/aptsources/distro.py b/aptsources/distro.py index 5630259f..45a7172d 100644 --- a/aptsources/distro.py +++ b/aptsources/distro.py @@ -349,8 +349,6 @@ class UbuntuDistribution(Distribution): # Store all available servers: # Name, URI, active mirrors = [] - import pdb - pdb.set_trace() if len(self.used_servers) < 1 or \ (len(self.used_servers) == 1 and \ compare_mirrors(self.used_servers[0], self.main_server)): -- cgit v1.2.3 From c9827eb8959b4514e122807257585efd6334cb9c Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Wed, 4 Apr 2007 11:32:24 +0200 Subject: * Fix the addition of of sources that are already enabled but not with all components - fix LP#98795 --- aptsources/sourceslist.py | 8 +++++++- debian/changelog | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'aptsources') diff --git a/aptsources/sourceslist.py b/aptsources/sourceslist.py index a10d5b68..16784ec4 100644 --- a/aptsources/sourceslist.py +++ b/aptsources/sourceslist.py @@ -248,12 +248,18 @@ class SourcesList: yield entry raise StopIteration - def add(self, type, uri, dist, comps, comment="", pos=-1, file=None): + def add(self, type, uri, dist, orig_comps, comment="", pos=-1, file=None): """ Add a new source to the sources.list. The method will search for existing matching repos and will try to reuse them as far as possible """ + + # create a working copy of the component list so that we can modify it later + # FIXME: perhaps could be done in a prittier way + comps = [] + for c in orig_comps: + comps.append(c) # check if we have this source already in the sources.list for source in self.list: if source.disabled == False and source.invalid == False and \ diff --git a/debian/changelog b/debian/changelog index 2d40777f..b5c54084 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,10 @@ +python-apt (0.6.20ubuntu16) feisty; urgency=low + + * Fix the addition of of sources that are already enabled but not with + all components - fix LP#98795 + + -- Sebastian Heinlein Wed, 4 Apr 2007 11:31:33 +0200 + python-apt (0.6.20ubuntu15) unstable; urgency=low [ Sebastian Heinlein ] -- cgit v1.2.3 From e7e07a42983a9f1c14f4f5811d9972933a53e2fb Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Sat, 7 Apr 2007 11:08:46 +0200 Subject: * the gettext domain of the iso codes changed --- aptsources/distro.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'aptsources') diff --git a/aptsources/distro.py b/aptsources/distro.py index 45a7172d..a5210ace 100644 --- a/aptsources/distro.py +++ b/aptsources/distro.py @@ -341,7 +341,7 @@ class UbuntuDistribution(Distribution): if self.countries.has_key(country): # TRANSLATORS: %s is a country return _("Server for %s") % \ - gettext.dgettext("iso-3166", + gettext.dgettext("iso_3166", self.countries[country].rstrip()).rstrip() else: return("%s" % server.rstrip("/ ")) -- cgit v1.2.3 From fd5ca3c33012ce51c4d8a181cc0810e50fe31e6d Mon Sep 17 00:00:00 2001 From: Sebastian Heinlein Date: Tue, 24 Apr 2007 07:41:05 +0200 Subject: * do not set the child base_uri to the parent one automatically, since we want to know if it a special server was forced * check if a server was forced for a child source before changing it --- aptsources/distinfo.py | 2 -- aptsources/distro.py | 10 ++++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'aptsources') diff --git a/aptsources/distinfo.py b/aptsources/distinfo.py index 674c1bf5..fdd063a5 100644 --- a/aptsources/distinfo.py +++ b/aptsources/distinfo.py @@ -181,8 +181,6 @@ class DistInfo: template.match_uri = nanny.match_uri if template.mirror_set == {}: template.mirror_set = nanny.mirror_set - if template.base_uri == None: - template.base_uri = nanny.base_uri elif field == 'Available': template.available = value elif field == 'RepositoryType': diff --git a/aptsources/distro.py b/aptsources/distro.py index a5210ace..9d3b4105 100644 --- a/aptsources/distro.py +++ b/aptsources/distro.py @@ -267,11 +267,13 @@ class Distribution: seen_binary = [] seen_source = [] self.default_server = uri - sources = [] - sources.extend(self.main_sources) - sources.extend(self.child_sources) - for source in sources: + for source in self.main_sources: change_server_of_source(source, uri, seen_binary) + for source in self.child_sources: + # Do not change the forces server of a child source + if source.template.base_uri == None or \ + source.template.base_uri != source.uri: + change_server_of_source(source, uri, seen_binary) for source in self.source_code_sources: change_server_of_source(source, uri, seen_source) -- cgit v1.2.3