summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--DistUpgrade/DistUpgrade.cfg40
-rw-r--r--DistUpgrade/DistUpgradeCache.py51
-rw-r--r--DistUpgrade/DistUpgradeConfigParser.py20
-rw-r--r--DistUpgrade/DistUpgradeControler.py93
-rw-r--r--DistUpgrade/DistUpgradeViewGtk.py4
-rw-r--r--DistUpgrade/DistUpgradeViewNonInteractive.py78
-rw-r--r--DistUpgrade/README32
-rwxr-xr-xDistUpgrade/dist-upgrade.py14
-rw-r--r--SoftwareProperties/__init__.py1
-rw-r--r--SoftwareProperties/aptsources.py2
-rw-r--r--UpdateManager/UpdateManager.py2
-rw-r--r--debian/changelog3
-rw-r--r--gnome-software-properties2
13 files changed, 288 insertions, 54 deletions
diff --git a/DistUpgrade/DistUpgrade.cfg b/DistUpgrade/DistUpgrade.cfg
new file mode 100644
index 00000000..a2720f49
--- /dev/null
+++ b/DistUpgrade/DistUpgrade.cfg
@@ -0,0 +1,40 @@
+[View]
+View=DistUpgradeViewGtk
+#View=DistUpgradeViewNonInteractive
+
+# Distro contains global information about the upgrade
+[Distro]
+# the meta-pkgs we support
+MetaPkgs=ubuntu-desktop, kubuntu-desktop, edubuntu-desktop, xubuntu-desktop
+BaseMetaPkgs=ubuntu-base
+PostUpgradePurge=xorg-common
+
+# information about the individual meta-pkgs
+[ubuntu-desktop]
+KeyDependencies=gdm, gnome-panel, ubuntu-artwork
+# those pkgs will be marked remove right after the distUpgrade in the cache
+PostUpgradeRemove=xchat, xscreensaver
+
+[kubuntu-desktop]
+KeyDependencies=kdm, kicker, kubuntu-artwork-usplash
+# those packages are marked as obsolete right after the upgrade
+ForcedObsoletes=ivman
+
+[edubuntu-desktop]
+KeyDependencies=edubuntu-artwork, tuxpaint
+
+[xubuntu-desktop]
+KeyDependencies=xubuntu-artwork-usplash, xubuntu-default-settings, xfce4
+
+
+[Files]
+BackupExt=distUpgrade
+
+[Sources]
+From=breezy
+To=dapper
+ValidOrigin=Ubuntu
+ValidMirrors = http://archive.ubuntu.com/ubuntu, http://security.ubuntu.com/ubuntu
+
+[Network]
+MaxRetries=3 \ No newline at end of file
diff --git a/DistUpgrade/DistUpgradeCache.py b/DistUpgrade/DistUpgradeCache.py
index c7cb89f3..b20c9f77 100644
--- a/DistUpgrade/DistUpgradeCache.py
+++ b/DistUpgrade/DistUpgradeCache.py
@@ -5,6 +5,7 @@ import os
import re
import logging
from gettext import gettext as _
+from DistUpgradeConfigParser import DistUpgradeConfig
class MyCache(apt.Cache):
# init
@@ -12,6 +13,10 @@ class MyCache(apt.Cache):
apt.Cache.__init__(self, progress)
self.to_install = []
self.to_remove = []
+
+ self.config = DistUpgradeConfig()
+ self.metapkgs = self.config.getlist("Distro","MetaPkgs")
+
# turn on debuging
apt_pkg.Config.Set("Debug::pkgProblemResolver","true")
fd = os.open(os.path.expanduser("~/dist-upgrade-apt.log"), os.O_RDWR|os.O_CREAT|os.O_TRUNC)
@@ -93,10 +98,38 @@ class MyCache(apt.Cache):
return False
return True
+ def markInstall(self, pkg, reason=""):
+ logging.debug("Installing '%s' (%s)" % (pkg, reason))
+ if self.has_key(pkg):
+ self[pkg].markInstall()
+ def markRemove(self, pkg, reason=""):
+ logging.debug("Removing '%s' (%s)" % (pkg, reason))
+ if self.has_key(pkg):
+ self[pkg].markDelete()
+ def markPurge(self, pkg, reason=""):
+ logging.debug("Purging '%s' (%s)" % (pkg, reason))
+ if self.has_key(pkg):
+ self._depcache.MarkDelete(self[pkg]._pkg,True)
+
+ def postUpgradeRule(self):
+ " run after the upgrade was done in the cache "
+ for (rule, action) in [("Install", self.markInstall),
+ ("Remove", self.markRemove),
+ ("Purge", self.markPurge)]:
+ # first the global list
+ for pkg in self.config.getlist("Distro","PostUpgrade%s" % rule):
+ action(pkg, "Distro PostUpgrade%s rule" % rule)
+ for key in self.metapkgs:
+ if self.has_key(key) and self[key].isInstalled:
+ for pkg in self.config.getlist(key,"PostUpgrade%s" % rule):
+ action(pkg, "%s PostUpgrade%s rule" % (key, rule))
+
def distUpgrade(self, view):
try:
# upgrade (and make sure this way that the cache is ok)
self.upgrade(True)
+ self.postUpgradeRule()
+
if not self._installMetaPkgs(view):
raise SystemError, _("Can't upgrade required meta-packages")
if not self._verifyChanges():
@@ -161,19 +194,19 @@ class MyCache(apt.Cache):
return metapkg_found
# now check for ubuntu-desktop, kubuntu-desktop, edubuntu-desktop
- metapkgs = {"ubuntu-desktop": ["gdm","gnome-panel", "ubuntu-artwork"],
- "kubuntu-desktop": ["kdm", "kicker",
- "kubuntu-artwork-usplash"],
- "edubuntu-desktop": ["edubuntu-artwork", "tuxpaint"]
- }
+ metapkgs = self.config.getlist("Distro","MetaPkgs")
# we never go without ubuntu-base
- self["ubuntu-base"].markInstall()
+ for pkg in self.config.getlist("Distro","BaseMetaPkgs"):
+ self[pkg].markInstall()
+
# every meta-pkg that is installed currently, will be marked
# install (that result in a upgrade and removes a markDelete)
for key in metapkgs:
try:
- if self[key].isInstalled: self[key].markUpgrade()
+ if self.has_key(key) and self[key].isInstalled:
+ logging.debug("Marking '%s' for upgrade" % key)
+ self[key].markUpgrade()
except SystemError, e:
logging.debug("Can't mark '%s' for upgrade" % key)
return False
@@ -182,7 +215,7 @@ class MyCache(apt.Cache):
logging.debug("no {ubuntu,edubuntu,kubuntu}-desktop pkg installed")
for key in metapkgs:
deps_found = True
- for pkg in metapkgs[key]:
+ for pkg in self.config.getlist(key,"KeyDependencies"):
deps_found &= self.has_key(pkg) and self[pkg].isInstalled
if deps_found:
logging.debug("guessing '%s' as missing meta-pkg" % key)
@@ -208,8 +241,6 @@ class MyCache(apt.Cache):
"above first using synaptic or "
"apt-get before proceeding."))
return False
-
- # FIXME: check for ubuntu-desktop, kubuntu-dekstop, edubuntu-desktop
return True
def _inRemovalBlacklist(self, pkgname):
diff --git a/DistUpgrade/DistUpgradeConfigParser.py b/DistUpgrade/DistUpgradeConfigParser.py
new file mode 100644
index 00000000..c87e2f1b
--- /dev/null
+++ b/DistUpgrade/DistUpgradeConfigParser.py
@@ -0,0 +1,20 @@
+from ConfigParser import ConfigParser, NoOptionError
+
+
+class DistUpgradeConfig(ConfigParser):
+ def __init__(self):
+ ConfigParser.__init__(self)
+ self.read(['DistUpgrade.cfg'])
+ def getlist(self, section, option):
+ try:
+ tmp = self.get(section, option)
+ except NoOptionError:
+ return []
+ items = [x.strip() for x in tmp.split(",")]
+ return items
+
+
+if __name__ == "__main__":
+ c = DistUpgradeConfigParser()
+ print c.getlist("Distro","MetaPkgs")
+ print c.getlist("Distro","ForcedPurges")
diff --git a/DistUpgrade/DistUpgradeControler.py b/DistUpgrade/DistUpgradeControler.py
index 6ab77103..9fa717fe 100644
--- a/DistUpgrade/DistUpgradeControler.py
+++ b/DistUpgrade/DistUpgradeControler.py
@@ -28,8 +28,8 @@ import subprocess
import logging
import re
import statvfs
+from DistUpgradeConfigParser import DistUpgradeConfig
-from UpdateManager.Common.SimpleGladeApp import SimpleGladeApp
from SoftwareProperties.aptsources import SourcesList, SourceEntry
from gettext import gettext as _
from DistUpgradeCache import MyCache
@@ -42,22 +42,16 @@ class DistUpgradeControler(object):
self._view.updateStatus(_("Reading cache"))
self.cache = None
+ self.config = DistUpgradeConfig()
+ self.sources_backup_ext = "."+self.config.get("Files","BackupExt")
+
# some constants here
- #self.fromDist = "hoary"
- #self.toDist = "breezy"
- self.fromDist = "breezy"
- self.toDist = "dapper"
-
- self.origin = "Ubuntu"
+ self.fromDist = self.config.get("Sources","From")
+ self.toDist = self.config.get("Sources","To")
+ self.origin = self.config.get("Sources","ValidOrigin")
# forced obsoletes
- self.forced_obsoletes = []
- for line in open("forced_obsoletes.txt").readlines():
- line = line.strip()
- if not line == "" or line.startswith("#"):
- self.forced_obsoletes.append(line)
- logging.debug("forced obsoletes '%s'" % line)
-
+ self.forced_obsoletes = self.config.getlist("Distro","ForcedObsoletes")
def openCache(self):
self.cache = MyCache(self._view.getOpCacheProgress())
@@ -81,15 +75,16 @@ class DistUpgradeControler(object):
]
# list of valid mirrors that we can add
- valid_mirrors = ["http://archive.ubuntu.com/ubuntu",
- "http://security.ubuntu.com/ubuntu"]
+ valid_mirrors = self.config.getlist("Sources","ValidMirrors")
# look over the stuff we have
foundToDist = False
for entry in self.sources:
# check if it's a mirror (or offical site)
+ validMirror = False
for mirror in valid_mirrors:
if self.sources.is_mirror(mirror,entry.uri):
+ validMirror = True
if entry.dist in toDists:
# so the self.sources.list is already set to the new
# distro
@@ -103,10 +98,9 @@ class DistUpgradeControler(object):
entry.disabled = True
# it can only be one valid mirror, so we can break here
break
- else:
- # disable non-official entries that point to dist
- if entry.dist == self.fromDist:
- entry.disabled = True
+ # disable anything that is not from a official mirror
+ if not validMirror:
+ entry.disabled = True
if not foundToDist:
# FIXME: offer to write a new self.sources.list entry
@@ -116,12 +110,14 @@ class DistUpgradeControler(object):
"the upgrade was found.\n"))
# write (well, backup first ;) !
- self.sources_backup_ext = ".distUpgrade"
self.sources.backup(self.sources_backup_ext)
self.sources.save()
# re-check if the written self.sources are valid, if not revert and
# bail out
+ # TODO: check if some main packages are still available or if we
+ # accidently shot them, if not, maybe offer to write a standard
+ # sources.list?
try:
sourceslist = apt_pkg.GetPkgSourceList()
sourceslist.ReadMainList()
@@ -160,17 +156,25 @@ class DistUpgradeControler(object):
progress = self._view.getFetchProgress()
# FIXME: retry here too? just like the DoDistUpgrade?
# also remove all files from the lists partial dir!
- try:
- res = self.cache.update(progress)
- except IOError, e:
- self._view.error(_("Error during update"),
- _("A problem occured during the update. "
- "This is usually some sort of network "
- "problem, please check your network "
- "connection and retry."),
- "%s" % e)
- return False
- return True
+ currentRetry = 0
+ maxRetries = self.config.get("Network","MaxRetries")
+ while currentRetry < maxRetries:
+ try:
+ res = self.cache.update(progress)
+ except IOError, e:
+ logging.error("IOError in cache.update(): '%s'. Retrying (currentRetry: %s)" % (e,currentRetry))
+ currentRetry += 1
+ continue
+ # no exception, so all was fine, we are done
+ return True
+
+ self._view.error(_("Error during update"),
+ _("A problem occured during the update. "
+ "This is usually some sort of network "
+ "problem, please check your network "
+ "connection and retry."), "%s" % e)
+ return False
+
def askDistUpgrade(self):
if not self.cache.distUpgrade(self._view):
@@ -193,12 +197,13 @@ class DistUpgradeControler(object):
self.cache.requiredDownload)
return res
- def doDistUpgrade(self, currentTry=0):
+ def doDistUpgrade(self):
currentRetry = 0
fprogress = self._view.getFetchProgress()
iprogress = self._view.getInstallProgress()
# retry the fetching in case of errors
- while currentRetry < 3:
+ maxRetries = self.config.get("Network","MaxRetries")
+ while currentRetry < maxRetries:
try:
res = self.cache.commit(fprogress,iprogress)
except SystemError, e:
@@ -212,7 +217,7 @@ class DistUpgradeControler(object):
return False
except IOError, e:
# fetch failed, will be retried
- logging.error("IOError in cache.commit(): '%s'. Retrying (currentTry: %s)" % (e,currentTry))
+ logging.error("IOError in cache.commit(): '%s'. Retrying (currentTry: %s)" % (e,currentRetry))
currentRetry += 1
continue
# no exception, so all was fine, we are done
@@ -239,18 +244,29 @@ class DistUpgradeControler(object):
now_foreign = self.cache._getForeignPkgs(self.origin, self.fromDist, self.toDist)
logging.debug("Obsolete: %s" % " ".join(now_obsolete))
logging.debug("Foreign: %s" % " ".join(now_foreign))
-
+
+ # now get the meta-pkg specific obsoletes and purges
+ for pkg in self.config.getlist("Distro","MetaPkgs"):
+ if self.cache.has_key(pkg) and self.cache[pkg].isInstalled:
+ self.forced_obsoletes.extend(self.config.getlist(pkg,"ForcedObsoletes"))
+ logging.debug("forced_obsoletes: %s", self.forced_obsoletes)
+
+
+
# mark packages that are now obsolete (and where not obsolete
# before) to be deleted. make sure to not delete any foreign
# (that is, not from ubuntu) packages
remove_candidates = now_obsolete - self.obsolete_pkgs
remove_candidates |= set(self.forced_obsoletes)
+ logging.debug("remove_candidates: '%s'" % remove_candidates)
logging.debug("Start checking for obsolete pkgs")
for pkgname in remove_candidates:
if pkgname not in self.foreign_pkgs:
if not self.cache._tryMarkObsoleteForRemoval(pkgname, remove_candidates, self.foreign_pkgs):
logging.debug("'%s' scheduled for remove but not in remove_candiates, skipping", pkgname)
logging.debug("Finish checking for obsolete pkgs")
+
+ # get changes
changes = self.cache.getChanges()
logging.debug("The following packages are remove candidates: %s" % " ".join([pkg.name for pkg in changes]))
if len(changes) > 0 and \
@@ -266,7 +282,6 @@ class DistUpgradeControler(object):
"Please see the below message for more "
"information. "),
"%s" % e)
- self.cache.commit(fprogress,iprogress)
def abort(self):
""" abort the upgrade, cleanup (as much as possible) """
@@ -280,9 +295,13 @@ class DistUpgradeControler(object):
self._view.updateStatus(_("Checking package manager"))
self._view.setStep(1)
self.openCache()
+
if not self.cache.sanityCheck(self._view):
abort(1)
+ # run a "apt-get update" now
+ self.doUpdate()
+
# do pre-upgrade stuff (calc list of obsolete pkgs etc)
self.doPreUpdate()
diff --git a/DistUpgrade/DistUpgradeViewGtk.py b/DistUpgrade/DistUpgradeViewGtk.py
index f32533db..b4ad4a0a 100644
--- a/DistUpgrade/DistUpgradeViewGtk.py
+++ b/DistUpgrade/DistUpgradeViewGtk.py
@@ -29,6 +29,7 @@ import gobject
import pango
import sys
import logging
+import time
import apt
import apt_pkg
@@ -169,9 +170,10 @@ class GtkInstallProgressAdapter(InstallProgress):
self.label_status.set_text(self.status)
while gtk.events_pending():
gtk.main_iteration()
+ time.sleep(0.01)
-class GtkDistUpgradeView(DistUpgradeView,SimpleGladeApp):
+class DistUpgradeViewGtk(DistUpgradeView,SimpleGladeApp):
" gtk frontend of the distUpgrade tool "
diff --git a/DistUpgrade/DistUpgradeViewNonInteractive.py b/DistUpgrade/DistUpgradeViewNonInteractive.py
new file mode 100644
index 00000000..7a8fa7eb
--- /dev/null
+++ b/DistUpgrade/DistUpgradeViewNonInteractive.py
@@ -0,0 +1,78 @@
+# DistUpgradeView.py
+#
+# Copyright (c) 2004,2005 Canonical
+#
+# Author: Michael Vogt <michael.vogt@ubuntu.com>
+#
+# 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 apt
+import logging
+import time
+from DistUpgradeView import DistUpgradeView
+
+class NonInteractiveInstallProgress(apt.progress.InstallProgress):
+ def error(self, pkg, errormsg):
+ logging.error("got a error from dpkg for pkg: '%s': '%s'" % (pkg, errormsg))
+ def conffile(self, current, new):
+ logging.debug("got a conffile-prompt from dpkg for file: '%s'" % current)
+ def updateInterface(self):
+ apt.progress.InstallProgress.updateInterface(self)
+ time.sleep(0.001)
+
+class DistUpgradeViewNonInteractive(DistUpgradeView):
+ " non-interactive version of the upgrade view "
+ def __init__(self):
+ pass
+ def getOpCacheProgress(self):
+ " return a OpProgress() subclass for the given graphic"
+ return apt.progress.OpProgress()
+ def getFetchProgress(self):
+ " return a fetch progress object "
+ return apt.progress.FetchProgress()
+ def getInstallProgress(self):
+ " return a install progress object "
+ return NonInteractiveInstallProgress()
+ def updateStatus(self, msg):
+ """ update the current status of the distUpgrade based
+ on the current view
+ """
+ pass
+ def setStep(self, step):
+ """ we have 5 steps current for a upgrade:
+ 1. Analyzing the system
+ 2. Updating repository information
+ 3. Performing the upgrade
+ 4. Post upgrade stuff
+ 5. Complete
+ """
+ pass
+ def confirmChanges(self, summary, changes, downloadSize):
+ DistUpgradeView.confirmChanges(self, summary, changes, downloadSize)
+ logging.debug("toinstall: '%s'" % self.toInstall)
+ logging.debug("toupgrade: '%s'" % self.toUpgrade)
+ logging.debug("toremove: '%s'" % self.toRemove)
+ return True
+ def askYesNoQuestion(self, summary, msg):
+ " ask a Yes/No question and return True on 'Yes' "
+ return True
+ def confirmRestart(self):
+ " generic ask about the restart, can be overriden "
+ return False
+ def error(self, summary, msg, extended_msg=None):
+ " display a error "
+ logging.error("%s %s (%s)" % (summary, msg, extended_msg))
+
diff --git a/DistUpgrade/README b/DistUpgrade/README
new file mode 100644
index 00000000..28684985
--- /dev/null
+++ b/DistUpgrade/README
@@ -0,0 +1,32 @@
+The DistUpgrade.cfg format is based on the python ConfigParser.
+
+It supports the following sections:
+
+[View] - controls the output
+
+[Distro] - global distribution specfic options
+BaseMetaPkgs:
+ the basic meta-pkgs that must be installed (ubuntu-base usually)
+MetaPkgs:
+ packages that define a "desktop" (e.g. ubuntu-desktop)
+PostUpgrade{Install,Remove,Purge}:
+ action right after the upgrade was calculated in the cache (marking
+ happens *before* the cache.commit())
+ForcedObsoletes:
+ Obsolete packages that the user is asked about after the upgrade (marking
+ happens *after* the cache.commit())
+
+[$meta-pkg]
+KeyDependencies:
+ Dependencies that are considered "key" dependencies of the meta-pkg to
+ detect if it was installed but later removed by the user
+PostUpgrade{Install,Remove,Purge}:
+ s.above
+ForcedObsoletes:
+ s.above
+
+[Files] - file specific stuff
+
+[Sources] - how to rewrite the sources.list
+
+[Network] - network specific options \ No newline at end of file
diff --git a/DistUpgrade/dist-upgrade.py b/DistUpgrade/dist-upgrade.py
index eff05f35..437be42d 100755
--- a/DistUpgrade/dist-upgrade.py
+++ b/DistUpgrade/dist-upgrade.py
@@ -1,9 +1,10 @@
#!/usr/bin/python2.4
-from DistUpgradeViewGtk import GtkDistUpgradeView
from DistUpgradeControler import DistUpgradeControler
+from DistUpgradeConfigParser import DistUpgradeConfig
import logging
import os
+import sys
if __name__ == "__main__":
@@ -12,7 +13,16 @@ if __name__ == "__main__":
format='%(asctime)s %(levelname)s %(message)s',
filemode='w')
- view = GtkDistUpgradeView()
+ config = DistUpgradeConfig()
+ requested_view= config.get("View","View")
+ try:
+ view_modul = __import__(requested_view)
+ view_class = getattr(view_modul, requested_view)
+ view = view_class()
+ except (ImportError, AttributeError):
+ logging.error("can't import view '%s'" % requested_view)
+ print "can't find %s" % requested_view
+ sys.exit(1)
app = DistUpgradeControler(view)
app.run()
diff --git a/SoftwareProperties/__init__.py b/SoftwareProperties/__init__.py
index 1e6834bd..e69de29b 100644
--- a/SoftwareProperties/__init__.py
+++ b/SoftwareProperties/__init__.py
@@ -1 +0,0 @@
-from SoftwareProperties import SoftwareProperties
diff --git a/SoftwareProperties/aptsources.py b/SoftwareProperties/aptsources.py
index 3e8d522e..c0d2c2ba 100644
--- a/SoftwareProperties/aptsources.py
+++ b/SoftwareProperties/aptsources.py
@@ -517,3 +517,5 @@ if __name__ == "__main__":
"http://de.archive.ubuntu.com/ubuntu/")
print "is_mirror(): %s" % mirror
+ print sources.is_mirror("http://archive.ubuntu.com/ubuntu",
+ "http://de.archive.ubuntu.com/ubuntu/")
diff --git a/UpdateManager/UpdateManager.py b/UpdateManager/UpdateManager.py
index 3eac6395..b87fb498 100644
--- a/UpdateManager/UpdateManager.py
+++ b/UpdateManager/UpdateManager.py
@@ -649,7 +649,7 @@ class UpdateManager(SimpleGladeApp):
# self.window_main.set_sensitive(True)
self.window_main.set_sensitive(False)
from SoftwareProperties import SoftwareProperties
- prop = SoftwareProperties(self.datadir, None)
+ prop = SoftwareProperties.SoftwareProperties(self.datadir, None)
prop.window_main.set_transient_for(self.window_main)
prop.run()
prop.window_main.hide()
diff --git a/debian/changelog b/debian/changelog
index 70e94e66..fc2375ef 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -3,8 +3,9 @@ update-manager (0.42.2ubuntu1) dapper; urgency=low
* UpdateManager/MetaRelease.py:
- never offer a upgrade to a unsupported (i.e. developer) dist
* data/gnome-software-properties.desktop.in: use X-KDE-SubstituteUID=true
+ * small UI layout changes (should fix the cancel/close button problem)
- -- Michael Vogt <michael.vogt@ubuntu.com> Fri, 27 Jan 2006 22:57:43 +0100
+ -- Michael Vogt <michael.vogt@ubuntu.com> Tue, 31 Jan 2006 09:48:13 +0000
update-manager (0.42.1ubuntu1) dapper; urgency=low
diff --git a/gnome-software-properties b/gnome-software-properties
index d5cbd0ce..a8074f7e 100644
--- a/gnome-software-properties
+++ b/gnome-software-properties
@@ -37,7 +37,7 @@ from optparse import OptionParser
#sys.path.append("@prefix@/share/update-manager/python")
-import SoftwareProperties
+from SoftwareProperties import SoftwareProperties
if __name__ == "__main__":
_ = gettext.gettext