diff options
| author | glatzor@ubuntu.com <> | 2006-08-02 18:37:59 +0200 |
|---|---|---|
| committer | glatzor@ubuntu.com <> | 2006-08-02 18:37:59 +0200 |
| commit | 0a5aacc19bfe31eda221e2f6476712097d693a06 (patch) | |
| tree | 8995ea136686477cc31a4e240b6464ea0c6d6ed0 /UpdateManager | |
| parent | 70bf03a91044f338aed8acd819c15dc8d5968103 (diff) | |
| parent | dde2af913b3f26958f93f604d7a75e61e64ec7f1 (diff) | |
| download | python-apt-0a5aacc19bfe31eda221e2f6476712097d693a06.tar.gz | |
* merge with main
Diffstat (limited to 'UpdateManager')
| -rw-r--r-- | UpdateManager/Common/DistInfo.py | 76 | ||||
| -rw-r--r-- | UpdateManager/Common/aptsources.py | 672 | ||||
| -rw-r--r-- | UpdateManager/DistUpgradeFetcher.py | 9 | ||||
| -rw-r--r-- | UpdateManager/MetaRelease.py | 10 | ||||
| -rw-r--r-- | UpdateManager/UpdateManager.py | 151 |
5 files changed, 824 insertions, 94 deletions
diff --git a/UpdateManager/Common/DistInfo.py b/UpdateManager/Common/DistInfo.py index 102b981c..7d2d37ad 100644 --- a/UpdateManager/Common/DistInfo.py +++ b/UpdateManager/Common/DistInfo.py @@ -28,21 +28,30 @@ import ConfigParser _ = gettext.gettext class Suite: - name = None - description = None - base_uri = None - repository_type = None - components = None + 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.distribution = None + self.available = True class Component: - name = None - description = None - enabled = None + def __init__(self): + self.name = "" + self.description = "" + self.description_long = "" + self.enabled = None class DistInfo: def __init__(self, dist = None, - base_dir = "/usr/share/update-manager/dists"): + base_dir = "/usr/share/update-manager/channels"): self.metarelease_uri = '' self.suites = [] @@ -73,43 +82,68 @@ class DistInfo: elif field == 'Suite': if suite: if component: - suite.components.append (component) + suite.components["%s" % component.name] = \ + (component.description, component.enabled, + component.description_long) component = None self.suites.append (suite) suite = Suite () suite.name = value - suite.components = [] + 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) + elif field == 'Available': + suite.available = value elif field == 'RepositoryType': - suite.repository_type = value + suite.type = value elif field == 'BaseURI': suite.base_uri = value + suite.match_uri = value + elif field == 'MatchURI': + suite.match_uri = value elif field == 'Description': suite.description = _(value) elif field == 'Component': if component: - suite.components.append (component) + suite.components["%s" % component.name] = \ + (component.description, component.enabled, + component.description_long) component = Component () component.name = value elif field == 'Enabled': component.enabled = bool(int(value)) elif field == 'CompDescription': component.description = _(value) + elif field == 'CompDescriptionLong': + component.description_long = _(value) if suite: if component: - suite.components.append (component) + suite.components["%s" % component.name] = \ + (component.description, component.enabled, + component.description_long) component = None self.suites.append (suite) suite = None if __name__ == "__main__": - d = DistInfo ("Debian", "../../channels") + d = DistInfo ("Ubuntu", "../../channels") print d.changelogs_uri for suite in d.suites: - print suite.name - print suite.description - print suite.base_uri + print "\nSuite: %s" % suite.name + print "Desc: %s" % suite.description + print "BaseURI: %s" % suite.base_uri + print "MatchURI: %s" % suite.match_uri for component in suite.components: - print component.name - print component.description - print component.enabled + print " %s - %s - %s - %s" % (component, + suite.components[component][0], + suite.components[component][1], + suite.components[component][2]) + for child in suite.children: + print " %s" % child.description diff --git a/UpdateManager/Common/aptsources.py b/UpdateManager/Common/aptsources.py new file mode 100644 index 00000000..3d317b15 --- /dev/null +++ b/UpdateManager/Common/aptsources.py @@ -0,0 +1,672 @@ +# aptsource.py.in - parse sources.list +# +# Copyright (c) 2004,2005 Canonical +# 2004 Michiel Sikkes +# +# Author: Michiel Sikkes <michiel@eyesopened.nl> +# Michael Vogt <mvo@debian.org> +# +# 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 pdb + +from UpdateManager.Common.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 "<country>." (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)) + + + +# actual source.list entries +class SourceEntry: + + def __init__(self, line,file=None): + self.invalid = False + self.disabled = False + self.type = "" + self.uri = "" + self.dist = "" + self.comps = [] + self.comment = "" + self.line = line + if file == None: + file = apt_pkg.Config.FindDir("Dir::Etc")+apt_pkg.Config.Find("Dir::Etc::sourcelist") + self.file = file + self.parse(line) + self.template = None + self.children = [] + + # works mostely like split but takes [] into account + def mysplit(self, line): + 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 + + + # parse a given source line and split it into the fields we need + def parse(self,line): + 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] == "deb" or pieces[0] == "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"): + 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 = [] + + #print self.__dict__ + + + # set enabled/disabled + def set_enabled(self, new_value): + 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 + +# the SourceList file as a class +class NullMatcher(object): + def match(self, s): + return True + +class SourcesList: + def __init__(self, withMatcher=True): + self.list = [] # of Type SourceEntries + if withMatcher: + self.matcher = SourceEntryMatcher() + else: + self.matcher = NullMatcher() + self.refresh() + + def refresh(self): + 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): + 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 + """ + 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): + 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): + 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: + 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) + +# templates for the add dialog +class SourceEntryTemplate(SourceEntry): + def __init__(self,a_type,uri,dist,description,comps): + self.comps_descriptions = [] + self.type = a_type + self.uri = uri + self.dist = dist + self.description = description + self.comps = comps + + def matches(self,source_entry): + """ check if a given source_entry matches this one """ + if (self.type != source_entry.type): + return False + if (self.dist != source_entry.dist): + return False + if not is_mirror(self.uri,source_entry.uri): + return False + for e_comp in source_entry.comps: + for t_comp in self.comps: + if e_comp == t_comp.name: break + else: + return False + return True + +class SourceCompTemplate: + def __init__(self, name, description, on_by_default): + self.name = name + self.description = description + self.on_by_default = on_by_default + +class SourceEntryTemplates: + def __init__(self,datadir): + _ = gettext.gettext + self.templates = [] + + dinfo = DistInfo (base_dir=datadir+"channels/") + + for suite in dinfo.suites: + comps = [] + for comp in suite.components: + comps.append(SourceCompTemplate(comp.name, _(comp.description), + comp.enabled)) + self.templates.append (SourceEntryTemplate(suite.repository_type, + suite.base_uri, + suite.name, + suite.description, + comps)) + +# matcher class to make a source entry look nice +# lots of predefined matchers to make it i18n/gettext friendly +class SourceEntryMatcher: + class MatchType: + def __init__(self, a_type,a_descr): + self.type = a_type + self.description = a_descr + + class MatchDist: + def __init__(self,a_uri,a_dist, a_descr,l_comps, l_comps_descr): + self.uri = a_uri + self.dist = a_dist + self.description = a_descr + self.comps = l_comps + self.comps_descriptions = l_comps_descr + + def __init__(self): + self.templates = [] + # Get the human readable channel and comp names from the channel .infos + spec_files = glob.glob("/usr/share/update-manager/channels/*.info") + for f in spec_files: + f = os.path.basename(f) + i = f.find(".info") + f = f[0:i] + dist = DistInfo(f) + 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: + #print "'%s'" %source.uri + if re.search(template.match_uri, source.uri) and \ + re.match(template.match_name, source.dist): + found = True + source.template = template + break + return found + +class Distribution: + def __init__(self): + """" + Container for distribution specific informations + """ + # LSB information + self.id = "" + self.codename = "" + self.description = "" + self.release = "" + + # get the LSB information + lsb_info = [] + for lsb_option in ["-i", "-c", "-d", "-r"]: + pipe = os.popen("lsb_release %s | cut -d : -f 2-" % lsb_option) + lsb_info.append(pipe.read().strip()) + del pipe + (self.id, self.codename, self.description, self.release) = lsb_info + + # 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] + except: + print "could not open file '%s'" % file + else: + f.close() + + def get_sources(self, sources_list): + """ + Find the corresponding template, main and child sources + for the distribution + """ + # 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 sources_list.matcher.templates: + if template.name == self.codename 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 sources_list.list: + if source.invalid == False and\ + source.dist == self.codename and\ + source.template and\ + source.template.name == self.codename: + #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 == "deb" and source.disabled == False: + self.main_sources.append(source) + comps.extend(source.comps) + media.append(source.uri) + elif source.type == "deb" and source.disabled == True: + self.disabled_sources.append(source) + elif source.type.endswith("-src") and source.disabled == False: + self.source_code_sources.append(source) + elif source.type.endswith("-src") and source.disabled == True: + self.disabled_sources.append(source) + if source.template in self.source_template.children: + #print "yeah! child found: %s" % source.template.name + if source.type == "deb": + self.child_sources.append(source) + elif source.type == "deb-src": + self.source_code_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 + + # try to guess the nearest mirror from the locale + # FIXME: for debian we need something different + if self.id == "Ubuntu": + 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] + else: + self.country = None + + # 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, sources_list, 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 = "deb" + if comment == "": + comment == "Added by software-properties" + new_source = sources_list.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 not type.endswith("-src"): + sources_list.add("%s-src" % type, uri, dist, comps, comment, + file=new_source.file, + pos=sources_list.list.index(new_source)+1) + + def enable_component(self, sourceslist, comp): + """ + Disable 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 + """ + sources = [] + sources.extend(self.main_sources) + sources.extend(self.child_sources) + sources.extend(self.source_code_sources) + # check if there is a main source at all + if len(self.main_sources) < 1: + # create a new main source + self.add_source(sourceslist, comps=["%s"%comp]) + else: + # add the comp to all main, child and source code sources + for source in sources: + if comp not in source.comps: + source.comps.append(comp) + if self.get_source_code == True: + for source in self.source_code_sources: + if comp not in source.comps: source.comps.append(comp) + + def disable_component(self, sourceslist, 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: + sourceslist.remove(source) + + +# 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/") diff --git a/UpdateManager/DistUpgradeFetcher.py b/UpdateManager/DistUpgradeFetcher.py index aa51066a..af07cfb4 100644 --- a/UpdateManager/DistUpgradeFetcher.py +++ b/UpdateManager/DistUpgradeFetcher.py @@ -105,15 +105,13 @@ class DistUpgradeFetcher(object): return True def gpgauthenticate(self, file, signature, - keyring='/etc/apt/trusted.gpg', - trustdb='/etc/apt/trustdb.gpg'): + keyring='/etc/apt/trusted.gpg'): """ authenticated a file against a given signature, if no keyring is given use the apt default keyring """ gpg = GnuPGInterface.GnuPG() gpg.options.extra_args = ['--no-options', '--no-default-keyring', - '--trustdb-name',trustdb, '--keyring', keyring] proc = gpg.run(['--verify', signature, file], create_fhs=['status','logger','stderr']) @@ -190,7 +188,10 @@ class DistUpgradeFetcher(object): def runDistUpgrader(self): #print "runing: %s" % script - os.execv(self.script,[]) + if os.getuid() != 0: + os.execv("/usr/bin/gksu",["gksu",self.script]) + else: + os.execv(self.script,[self.script]) def cleanup(self): # cleanup diff --git a/UpdateManager/MetaRelease.py b/UpdateManager/MetaRelease.py index 57a808ee..70993eaf 100644 --- a/UpdateManager/MetaRelease.py +++ b/UpdateManager/MetaRelease.py @@ -60,8 +60,15 @@ class MetaRelease(gobject.GObject): def __init__(self, useDevelopmentRelase=False): gobject.GObject.__init__(self) + # check what uri to use if useDevelopmentRelase: self.METARELEASE_URI = self.METARELEASE_URI_UNSTABLE + # check if we can access the METARELEASE_FILE + if not os.access(self.METARELEASE_FILE, os.F_OK|os.W_OK|os.R_OK): + path = os.path.expanduser("~/.update-manager/") + if not os.path.exists(path): + os.mkdir(path) + self.METARELEASE_FILE = os.path.join(path,"meta-release") self.metarelease_information = None self.downloading = True # we start the download thread here and we have a timeout @@ -168,6 +175,3 @@ class MetaRelease(gobject.GObject): if os.path.exists(self.METARELEASE_FILE): f=open(self.METARELEASE_FILE,"r") -# register in the gobject system, needed for older versions of pygtk, -# never ones do this automatically -gobject.type_register(MetaRelease) diff --git a/UpdateManager/UpdateManager.py b/UpdateManager/UpdateManager.py index d4cc7a59..2e2433a8 100644 --- a/UpdateManager/UpdateManager.py +++ b/UpdateManager/UpdateManager.py @@ -69,8 +69,7 @@ from MetaRelease import Dist, MetaRelease # - kill "all_changes" and move the changes into the "Update" class # list constants -(LIST_INSTALL, LIST_CONTENTS, LIST_NAME, LIST_SHORTDESC, - LIST_VERSION, LIST_LONG_DESCR, LIST_PKG) = range(7) +(LIST_INSTALL, LIST_CONTENTS, LIST_NAME, LIST_PKG) = range(4) # actions for "invoke_manager" (INSTALL, UPDATE) = range(2) @@ -83,11 +82,19 @@ CHANGELOGS_URI="http://changelogs.ubuntu.com/changelogs/pool/%s/%s/%s/%s_%s/chan class MyCache(apt.Cache): def __init__(self, progress): apt.Cache.__init__(self, progress) + self._initDepCache() assert self._depcache.BrokenCount == 0 and self._depcache.DelCount == 0 self.all_changes = {} + def _initDepCache(self): + #apt_pkg.Config.Set("Debug::pkgPolicy","1") + #self.depcache = apt_pkg.GetDepCache(self.cache) + #self._depcache = apt_pkg.GetDepCache(self._cache) + self._depcache.ReadPinFile() + if os.path.exists(SYNAPTIC_PINFILE): + self._depcache.ReadPinFile(SYNAPTIC_PINFILE) + self._depcache.Init() def clean(self): - for pkg in self: - pkg.markKeep() + self._initDepCache() def saveDistUpgrade(self): """ this functions mimics a upgrade but will never remove anything """ self._depcache.Upgrade(True) @@ -193,23 +200,44 @@ class MyCache(apt.Cache): lock.release() class UpdateList: + ORIGIN_MAPPING = { ("edgy-security","Ubuntu"): _("Ubuntu security updates"), + ("edgy-updates","Ubuntu"): _("Ubuntu important updates"), + ("edgy-backports","Ubuntu"): _("Ubuntu backports"), + ("edgy","Ubuntu"): _("Ubuntu updates") + } + def __init__(self, parent_window): - self.pkgs = [] + # a map of packages under their origin + self.pkgs = {} self.num_updates = 0 self.parent_window = parent_window def update(self, cache): held_back = [] broken = [] + + # do the upgrade cache.saveDistUpgrade() + + # sort by origin for pkg in cache: if pkg.markedUpgrade or pkg.markedInstall: - self.pkgs.append(pkg) + originstr = _("Unknown") + for aorigin in pkg.candidateOrigin: + archive = aorigin.archive + origin = aorigin.origin + if self.ORIGIN_MAPPING.has_key((archive,origin)) and aorigin.trusted: + originstr = self.ORIGIN_MAPPING[(archive,origin)] + if not self.pkgs.has_key(originstr): + self.pkgs[originstr] = [] + self.pkgs[originstr].append(pkg) self.num_updates = self.num_updates + 1 elif pkg.isUpgradable: - #print "MarkedKeep: %s " % pkg.name held_back.append(pkg.name) - self.pkgs.sort(lambda x,y: cmp(x.name,y.name)) + for l in self.pkgs.keys(): + self.pkgs[l].sort(lambda x,y: cmp(x.name,y.name)) + + # check if we have held-back something if cache._depcache.KeepCount > 0: #print "WARNING, keeping packages" msg = ("<big><b>%s</b></big>\n\n%s" % \ @@ -287,8 +315,7 @@ class UpdateManager(SimpleGladeApp): self.button_close.connect("clicked", lambda w: self.exit()) # the treeview (move into it's own code!) - self.store = gtk.ListStore(gobject.TYPE_BOOLEAN, str, str, str, str, str, - gobject.TYPE_PYOBJECT) + self.store = gtk.ListStore(gobject.TYPE_BOOLEAN, str, str, gobject.TYPE_PYOBJECT) self.treeview_update.set_model(self.store) self.treeview_update.set_headers_clickable(True); @@ -299,20 +326,23 @@ class UpdateManager(SimpleGladeApp): cr.set_property("activatable", True) cr.set_property("xpad", 10) cr.connect("toggled", self.toggled) - self.cb = gtk.TreeViewColumn("Install", cr, active=LIST_INSTALL) - c0 = gtk.TreeViewColumn("Name", tr, markup=LIST_CONTENTS) - c0.set_resizable(True) + + column_install = gtk.TreeViewColumn("Install", cr) + column_install.set_cell_data_func (cr, self.install_column_view_func) + column = gtk.TreeViewColumn("Name", tr, markup=LIST_CONTENTS) + column.set_cell_data_func (tr, self.package_column_view_func) + column.set_resizable(True) major,minor,patch = gtk.pygtk_version if (major >= 2) and (minor >= 5): - self.cb.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) - self.cb.set_fixed_width(30) - c0.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) - c0.set_fixed_width(100) + column_install.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) + column_install.set_fixed_width(30) + column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) + column.set_fixed_width(100) #self.treeview_update.set_fixed_height_mode(True) - self.treeview_update.append_column(self.cb) - self.cb.set_visible(True) - self.treeview_update.append_column(c0) + self.treeview_update.append_column(column_install) + column_install.set_visible(True) + self.treeview_update.append_column(column) self.treeview_update.set_search_column(LIST_NAME) @@ -348,6 +378,24 @@ class UpdateManager(SimpleGladeApp): self.restore_state() self.window_main.show() + def header_column_func(self, cell_layot, renderer, model, iter): + pkg = model.get_value(iter, LIST_PKG) + if pkg == None: + renderer.set_property("cell-background","yellow") + else: + renderer.set_property("cell-background", None) + + def install_column_view_func(self, cell_layout, renderer, model, iter): + self.header_column_func(cell_layout, renderer, model, iter) + pkg = model.get_value(iter, LIST_PKG) + to_install = model.get_value(iter, LIST_INSTALL) + renderer.set_property("active", to_install) + # hide it if we are only a header line + renderer.set_property("visible", pkg != None) + + def package_column_view_func(self, cell_layout, renderer, model, iter): + self.header_column_func(cell_layout, renderer, model, iter) + def setupDbus(self): """ this sets up a dbus listener if none is installed alread """ # check if there is another g-a-i already and if not setup one @@ -389,24 +437,17 @@ class UpdateManager(SimpleGladeApp): return for line in lines: - end_iter = changes_buffer.get_end_iter() - - version_match = re.match(r'^%s \((.*)\)(.*)$' % re.escape(srcpkg), line) + version_match = re.match(r'^%s \((.*)\)(.*)\;.*$' % re.escape(srcpkg), line) #bullet_match = re.match("^.*[\*-]", line) author_match = re.match("^.*--.*<.*@.*>.*$", line) if version_match: version = version_match.group(1) + upload_archive = version_match.group(2).strip() version_text = _("Version %s: \n") % version changes_buffer.insert_with_tags_by_name(end_iter, version_text, "versiontag") - # mvo: disabled for now as it does not catch multi line entries - # (see ubuntu #7034 for rational) - #elif bullet_match and not author_match: - # bullet_text = " " + line + "\n" - # changes_buffer.insert(end_iter, bullet_text) elif (author_match): pass - #chanages_buffer.insert(end_iter, "\n") else: changes_buffer.insert(end_iter, line+"\n") @@ -421,7 +462,10 @@ class UpdateManager(SimpleGladeApp): iter = model.get_iter(path) # set descr - long_desc = model.get_value(iter, LIST_LONG_DESCR) + pkg = model.get_value(iter, LIST_PKG) + if pkg == None: + return + long_desc = pkg.description if long_desc == None: return # Skip the first line - it's a duplicate of the summary @@ -518,7 +562,7 @@ class UpdateManager(SimpleGladeApp): self.notebook_details.set_sensitive(True) self.treeview_update.set_sensitive(True) self.button_install.grab_default() - self.treeview_update.set_cursor(0) + self.treeview_update.set_cursor(1) self.label_header.set_markup(text_header) self.label_downsize.set_markup(text_download) @@ -649,17 +693,16 @@ class UpdateManager(SimpleGladeApp): self.list.update(self.cache) if self.list.num_updates > 0: i=0 - for pkg in self.list.pkgs: - - name = xml.sax.saxutils.escape(pkg.name) - summary = xml.sax.saxutils.escape(pkg.summary) - contents = "<big><b>%s</b></big>\n<small>%s\n\n" % (name, summary) - contents = contents + _("New version: %s (Size: %s)") % (pkg.candidateVersion,apt.SizeToStr(pkg.packageSize)) + "</small>" - - iter = self.store.append([True, contents, pkg.name, pkg.summary, - pkg.candidateVersion, pkg.description, pkg]) - self.add_update(pkg) - i = i + 1 + for origin in self.list.pkgs.keys(): + self.store.append([False, '<span weight="bold" size="large">%s</span>' % origin, origin, None]) + for pkg in self.list.pkgs[origin]: + name = xml.sax.saxutils.escape(pkg.name) + summary = xml.sax.saxutils.escape(pkg.summary) + contents = "<big><b>%s</b></big>\n<small>%s\n\n" % (name, summary) + contents = contents + _("New version: %s (Size: %s)") % (pkg.candidateVersion,apt.SizeToStr(pkg.packageSize)) + "</small>" + iter = self.store.append([True, contents, pkg.name, pkg]) + self.add_update(pkg) + i = i + 1 self.update_count() # use the normal cursor @@ -687,23 +730,6 @@ class UpdateManager(SimpleGladeApp): fetcher.run() def new_dist_available(self, meta_release, upgradable_to): - #print "new_dist_available: %s" % upgradable_to.name - # check if the user already knowns about this dist - #seen = self.gconfclient.get_string("/apps/update-manager/seen_dist") - #if name == seen: - # return - - #msg = "<big><b>%s</b></big>\n\n%s" % (_("There is a new release of Ubuntu available!"), _("A new release with the codename '%s' is available. Please see http://www.ubuntulinux.org/ for upgrade instructions.") % name) - #dialog = gtk.MessageDialog(self.window_main, 0, gtk.MESSAGE_INFO, - # gtk.BUTTONS_CLOSE, "") - #dialog.set_markup(msg) - #check = gtk.CheckButton(_("Never show this message again")) - #check.show() - #dialog.vbox.pack_start(check) - #dialog.run() - #if check.get_active(): - # self.gconfclient.set_string("/apps/update-manager/seen_dist",name) - #dialog.destroy() self.frame_new_release.show() self.label_new_release.set_markup(_("<b>New distribution release '%s' is available</b>") % upgradable_to.version) self.new_dist = upgradable_to @@ -755,13 +781,6 @@ class UpdateManager(SimpleGladeApp): sys.exit(1) else: progress.hide() - #apt_pkg.Config.Set("Debug::pkgPolicy","1") - #self.depcache = apt_pkg.GetDepCache(self.cache) - self.cache._depcache.ReadPinFile() - if os.path.exists(SYNAPTIC_PINFILE): - self.cache._depcache.ReadPinFile(SYNAPTIC_PINFILE) - self.cache._depcache.Init() - def check_auto_update(self): # Check if automatic update is enabled. If not show a dialog to inform |
