From 030068ade6a4e689a0e1b1c21a9151a3151fd735 Mon Sep 17 00:00:00 2001
From: Tomas Kopecek <tkopecek@redhat.com>
Date: Mar 01 2017 12:50:40 +0000
Subject: use new exception syntax


---

diff --git a/builder/kojid b/builder/kojid
index f3ec853..ea59c54 100755
--- a/builder/kojid
+++ b/builder/kojid
@@ -179,7 +179,7 @@ class BuildRoot(object):
              setup_dns=False, bind_opts=None, maven_opts=None, maven_envs=None, deps=None):
         """Create a brand new repo"""
         if not repo_id:
-            raise koji.BuildrootError, "A repo id must be provided"
+            raise koji.BuildrootError("A repo id must be provided")
         repo_info = self.session.repoInfo(repo_id, strict=True)
         self.repo_info = repo_info
         self.repoid = self.repo_info['id']
@@ -191,15 +191,15 @@ class BuildRoot(object):
         self.tag_id = self.config['id']
         self.tag_name = self.config['name']
         if self.config['id'] != repo_info['tag_id']:
-            raise koji.BuildrootError, "tag/repo mismatch: %s vs %s" \
-                    % (self.config['name'], repo_info['tag_name'])
+            raise koji.BuildrootError("tag/repo mismatch: %s vs %s" \
+                    % (self.config['name'], repo_info['tag_name']))
         repo_state = koji.REPO_STATES[repo_info['state']]
         if repo_state == 'EXPIRED':
             # This should be ok. Expired repos are still intact, just not
             # up-to-date (which may be the point in some cases).
             self.logger.info("Requested repo (%i) is no longer current" % repo_id)
         elif repo_state != 'READY':
-            raise koji.BuildrootError, "Requested repo (%i) is %s" % (repo_id, repo_state)
+            raise koji.BuildrootError("Requested repo (%i) is %s" % (repo_id, repo_state))
         self.br_arch = koji.canonArch(arch)
         # armhfp is not a valid arch according to autoconf
         if arch == 'armhfp':
@@ -209,7 +209,7 @@ class BuildRoot(object):
         self.logger.debug("New buildroot: %(tag_name)s/%(br_arch)s/%(repoid)s" % vars(self))
         id = self.session.host.newBuildRoot(self.repoid, self.br_arch, task_id=task_id)
         if id is None:
-            raise koji.BuildrootError, "failed to get a buildroot id"
+            raise koji.BuildrootError("failed to get a buildroot id")
         self.id = id
         self.name = "%(tag_name)s-%(id)s-%(repoid)s" % vars(self)
         self.install_group = install_group
@@ -462,7 +462,7 @@ class BuildRoot(object):
 
         if rv:
             self.expire()
-            raise koji.BuildrootError, "could not init mock buildroot, %s" % self._mockResult(rv)
+            raise koji.BuildrootError("could not init mock buildroot, %s" % self._mockResult(rv))
         self.session.host.setBuildRootList(self.id,self.getPackageList())
 
     def _mockResult(self, rv, logfile=None):
@@ -491,7 +491,7 @@ class BuildRoot(object):
             rv = self.mock(args)
             if rv:
                 self.expire()
-                raise koji.BuildError, "error retrieving sources, %s" % self._mockResult(rv)
+                raise koji.BuildError("error retrieving sources, %s" % self._mockResult(rv))
 
         args = ['--no-clean', '--buildsrpm', '--spec', specfile, '--sources', sources_dir,
                 '--target', 'noarch']
@@ -500,7 +500,7 @@ class BuildRoot(object):
 
         if rv:
             self.expire()
-            raise koji.BuildError, "error building srpm, %s" % self._mockResult(rv)
+            raise koji.BuildError("error building srpm, %s" % self._mockResult(rv))
 
     def build(self,srpm,arch=None):
         # run build
@@ -514,7 +514,7 @@ class BuildRoot(object):
         self.session.host.updateBuildRootList(self.id,self.getPackageList())
         if rv:
             self.expire()
-            raise koji.BuildError, "error building package (arch %s), %s" % (arch, self._mockResult(rv))
+            raise koji.BuildError("error building package (arch %s), %s" % (arch, self._mockResult(rv)))
 
     def getPackageList(self):
         """Return a list of packages from the buildroot
@@ -580,8 +580,8 @@ class BuildRoot(object):
             if maven_files:
                 path_comps = relpath.split('/')
                 if len(path_comps) < 3:
-                    raise koji.BuildrootError, 'files found in unexpected path in local Maven repo, directory: %s, files: %s' % \
-                        (relpath, ', '.join([f['filename'] for f in maven_files]))
+                    raise koji.BuildrootError('files found in unexpected path in local Maven repo, directory: %s, files: %s' % \
+                        (relpath, ', '.join([f['filename'] for f in maven_files])))
                 # extract the Maven info from the path within the local repo
                 maven_info = {'version': path_comps[-1],
                               'artifact_id': path_comps[-2],
@@ -621,11 +621,11 @@ class BuildRoot(object):
                                                    extra_deps=self.deps)
         if rv:
             self.expire()
-            raise koji.BuildrootError, 'error building Maven package, %s' % self._mockResult(rv, logfile='root.log')
+            raise koji.BuildrootError('error building Maven package, %s' % self._mockResult(rv, logfile='root.log'))
 
     def scrub(self):
         "Non-mock implementation of clean"
-        raise koji.FunctionDeprecated, "no longer needed and deprecated. use clean()"
+        raise koji.FunctionDeprecated("no longer needed and deprecated. use clean()")
 
     def markExternalRPMs(self, rpmlist):
         """Check rpms against pkgorigins and add external repo data to the external ones
@@ -654,7 +654,7 @@ class BuildRoot(object):
         try:
             repodata = repoMDObject.RepoMD('ourrepo', fo)
         except:
-            raise koji.BuildError, "Unable to parse repomd.xml file for %s" % os.path.join(repodir, self.br_arch)
+            raise koji.BuildError("Unable to parse repomd.xml file for %s" % os.path.join(repodir, self.br_arch))
         data  = repodata.getData('origin')
         pkgorigins  = data.location[1]
 
@@ -682,13 +682,13 @@ class BuildRoot(object):
             # worry about fixing the arch for them
             ext_url = origin_idx.get(key)
             if not ext_url:
-                raise koji.BuildError, "No origin for %s" % key
+                raise koji.BuildError("No origin for %s" % key)
             erepo = erepo_idx.get(ext_url)
             if not erepo:
                 if ext_url.startswith('file://') and ext_url.endswith(localtail):
                     # internal rpm
                     continue
-                raise koji.BuildError, "Unknown origin for %s: %s" % (key, ext_url)
+                raise koji.BuildError("Unknown origin for %s: %s" % (key, ext_url))
             rpm_info['external_repo'] = erepo
             rpm_info['location'] = erepo['external_repo_id']
 
@@ -721,10 +721,10 @@ class ChainBuildTask(BaseTaskHandler):
             and gotten into the repo.
         """
         if opts.get('scratch'):
-            raise koji.BuildError, "--scratch is not allowed with chain-builds"
+            raise koji.BuildError("--scratch is not allowed with chain-builds")
         target_info = self.session.getBuildTarget(target)
         if not target_info:
-            raise koji.GenericError, 'unknown build target: %s' % target
+            raise koji.GenericError('unknown build target: %s' % target)
         nvrs = []
         for n_level, build_level in enumerate(srcs):
             #if there are any nvrs to wait on, do so
@@ -769,14 +769,14 @@ class BuildTask(BaseTaskHandler):
             opts = {}
         self.opts = opts
         if opts.get('arch_override') and not opts.get('scratch'):
-            raise koji.BuildError, "arch_override is only allowed for scratch builds"
+            raise koji.BuildError("arch_override is only allowed for scratch builds")
         if opts.get('repo_id') is not None:
             repo_info = self.session.repoInfo(opts['repo_id'])
             if not repo_info:
-                raise koji.BuildError, 'No such repo: %s' % opts['repo_id']
+                raise koji.BuildError('No such repo: %s' % opts['repo_id'])
             repo_state = koji.REPO_STATES[repo_info['state']]
             if repo_state not in ('READY', 'EXPIRED'):
-                raise koji.BuildError, 'Bad repo: %s (%s)' % (repo_info['id'], repo_state)
+                raise koji.BuildError('Bad repo: %s (%s)' % (repo_info['id'], repo_state))
             self.event_id = repo_info['create_event']
         else:
             repo_info = None
@@ -792,13 +792,13 @@ class BuildTask(BaseTaskHandler):
             if repo_info is not None:
                 #make sure specified repo matches target
                 if repo_info['tag_id'] != target_info['build_tag']:
-                    raise koji.BuildError, 'Repo/Target mismatch: %s/%s' \
-                            % (repo_info['tag_name'], target_info['build_tag_name'])
+                    raise koji.BuildError('Repo/Target mismatch: %s/%s' \
+                            % (repo_info['tag_name'], target_info['build_tag_name']))
         else:
             # if repo_id is specified, we can allow the 'target' arg to simply specify
             # the destination tag (since the repo specifies the build tag).
             if repo_info is None:
-                raise koji.GenericError, 'unknown build target: %s' % target
+                raise koji.GenericError('unknown build target: %s' % target)
             build_tag = repo_info['tag_id']
             if target is None:
                 #ok, call it skip-tag for the buildroot tag
@@ -807,7 +807,7 @@ class BuildTask(BaseTaskHandler):
             else:
                 taginfo = self.session.getTag(target, event=self.event_id)
                 if not taginfo:
-                    raise koji.GenericError, 'neither tag nor target: %s' % target
+                    raise koji.GenericError('neither tag nor target: %s' % target)
                 dest_tag = taginfo['id']
         #policy checks...
         policy_data = {
@@ -843,11 +843,11 @@ class BuildTask(BaseTaskHandler):
         if not self.opts.get('skip_tag') and not self.opts.get('scratch'):
             # Make sure package is on the list for this tag
             if pkg_cfg is None:
-                raise koji.BuildError, "package %s not in list for tag %s" \
-                        % (data['name'], target_info['dest_tag_name'])
+                raise koji.BuildError("package %s not in list for tag %s" \
+                        % (data['name'], target_info['dest_tag_name']))
             elif pkg_cfg['blocked']:
-                raise koji.BuildError, "package %s is blocked for tag %s" \
-                        % (data['name'], target_info['dest_tag_name'])
+                raise koji.BuildError("package %s is blocked for tag %s" \
+                        % (data['name'], target_info['dest_tag_name']))
             # TODO - more pre tests
         archlist = self.getArchList(build_tag, h, extra=extra_arches)
         #let the system know about the build we're attempting
@@ -885,7 +885,7 @@ class BuildTask(BaseTaskHandler):
                 #assume this is a path under uploads
                 return src
         else:
-            raise koji.BuildError, 'Invalid source specification: %s' % src
+            raise koji.BuildError('Invalid source specification: %s' % src)
             #XXX - other methods?
 
     def getSRPMFromSCM(self, url, build_tag, repo_id):
@@ -909,7 +909,7 @@ class BuildTask(BaseTaskHandler):
         h = koji.get_rpm_header(fo)
         fo.close()
         if h[rpm.RPMTAG_SOURCEPACKAGE] != 1:
-            raise koji.BuildError, "%s is not a source package" % srpm
+            raise koji.BuildError("%s is not a source package" % srpm)
         return h
 
     def getArchList(self, build_tag, h, extra=None):
@@ -918,7 +918,7 @@ class BuildTask(BaseTaskHandler):
         arches = buildconfig['arches']
         if not arches:
             #XXX - need to handle this better
-            raise koji.BuildError, "No arches for tag %(name)s [%(id)s]" % buildconfig
+            raise koji.BuildError("No arches for tag %(name)s [%(id)s]" % buildconfig)
         tag_archlist = [koji.canonArch(a) for a in arches.split()]
         self.logger.debug('arches: %s' % arches)
         if extra:
@@ -955,7 +955,7 @@ class BuildTask(BaseTaskHandler):
             if a == 'noarch' or koji.canonArch(a) in tag_archlist:
                 archdict[a] = 1
         if not archdict:
-            raise koji.BuildError, "No matching arches were found"
+            raise koji.BuildError("No matching arches were found")
         return archdict.keys()
 
 
@@ -1037,7 +1037,7 @@ class BuildTask(BaseTaskHandler):
                 logs.setdefault(arch,[]).append(fn)
             if result['srpms']:
                 if built_srpm:
-                    raise koji.BuildError, "multiple builds returned a srpm.  task %i" % self.id
+                    raise koji.BuildError("multiple builds returned a srpm.  task %i" % self.id)
                 else:
                     built_srpm = result['srpms'][0]
                     brmap[result['srpms'][0]] = brootid
@@ -1119,11 +1119,11 @@ class BuildArchTask(BaseBuildTask):
         header = koji.get_rpm_header(filename)
 
         if not header[rpm.RPMTAG_PACKAGER]:
-            raise koji.BuildError, "The build system failed to set the packager tag"
+            raise koji.BuildError("The build system failed to set the packager tag")
         if not header[rpm.RPMTAG_VENDOR]:
-            raise koji.BuildError, "The build system failed to set the vendor tag"
+            raise koji.BuildError("The build system failed to set the vendor tag")
         if not header[rpm.RPMTAG_DISTRIBUTION]:
-            raise koji.BuildError, "The build system failed to set the distribution tag"
+            raise koji.BuildError("The build system failed to set the distribution tag")
 
     def handler(self, pkg, root, arch, keep_srpm, opts=None):
         """Build a package in a buildroot for one arch"""
@@ -1132,7 +1132,7 @@ class BuildArchTask(BaseBuildTask):
             opts = {}
         repo_id = opts.get('repo_id')
         if not repo_id:
-            raise koji.BuildError, "A repo id must be provided"
+            raise koji.BuildError("A repo id must be provided")
         repo_info = self.session.repoInfo(repo_id, strict=True)
         event_id = repo_info['create_event']
 
@@ -1140,7 +1140,7 @@ class BuildArchTask(BaseBuildTask):
         self.logger.debug("Reading SRPM")
         fn = self.localPath("work/%s" % pkg)
         if not os.path.exists(fn):
-            raise koji.BuildError, "SRPM file missing: %s" % fn
+            raise koji.BuildError("SRPM file missing: %s" % fn)
         # peel E:N-V-R from package
         h = koji.get_rpm_header(fn)
         name = h[rpm.RPMTAG_NAME]
@@ -1148,7 +1148,7 @@ class BuildArchTask(BaseBuildTask):
         rel = h[rpm.RPMTAG_RELEASE]
         epoch = h[rpm.RPMTAG_EPOCH]
         if h[rpm.RPMTAG_SOURCEPACKAGE] != 1:
-            raise koji.BuildError, "not a source package"
+            raise koji.BuildError("not a source package")
         # Disable checking for distribution in the initial SRPM because it
         # might have been built outside of the build system
         # if not h[rpm.RPMTAG_DISTRIBUTION]:
@@ -1204,9 +1204,9 @@ class BuildArchTask(BaseBuildTask):
         self.logger.debug("keep srpm %i %s %s" % (self.id, keep_srpm, opts))
         if keep_srpm:
             if len(srpm_files) == 0:
-                raise koji.BuildError, "no srpm files found for task %i" % self.id
+                raise koji.BuildError("no srpm files found for task %i" % self.id)
             if len(srpm_files) > 1:
-                raise koji.BuildError, "mulitple srpm files found for task %i: %s" % (self.id, srpm_files)
+                raise koji.BuildError("mulitple srpm files found for task %i: %s" % (self.id, srpm_files))
 
             # Run sanity checks.  Any failures will throw a BuildError
             self.srpm_sanity_checks("%s/%s" % (resultdir,srpm_files[0]))
@@ -1243,7 +1243,7 @@ class MavenTask(MultiPlatformTask):
         self.opts = opts
         target_info = self.session.getBuildTarget(target)
         if not target_info:
-            raise koji.BuildError, 'unknown build target: %s' % target
+            raise koji.BuildError('unknown build target: %s' % target)
         dest_tag = self.session.getTag(target_info['dest_tag'], strict=True)
         build_tag = self.session.getTag(target_info['build_tag'], strict=True)
 
@@ -1253,7 +1253,7 @@ class MavenTask(MultiPlatformTask):
             if repo:
                 repo_id = repo['id']
             else:
-                raise koji.BuildError, 'no repo for tag %s' % build_tag['name']
+                raise koji.BuildError('no repo for tag %s' % build_tag['name'])
 
         build_opts = dslice(opts, ['goals', 'profiles', 'properties', 'envs', 'patches',
                                    'packages', 'jvm_options', 'maven_options', 'deps'],
@@ -1272,18 +1272,18 @@ class MavenTask(MultiPlatformTask):
         if not self.opts.get('scratch'):
             maven_info = maven_results['maven_info']
             if maven_info['version'].endswith('-SNAPSHOT'):
-                raise koji.BuildError, '-SNAPSHOT versions are only supported in scratch builds'
+                raise koji.BuildError('-SNAPSHOT versions are only supported in scratch builds')
             build_info = koji.maven_info_to_nvr(maven_info)
 
             if not self.opts.get('skip_tag'):
                 dest_cfg = self.session.getPackageConfig(dest_tag['id'], build_info['name'])
                 # Make sure package is on the list for this tag
                 if dest_cfg is None:
-                    raise koji.BuildError, "package %s not in list for tag %s" \
-                        % (build_info['name'], dest_tag['name'])
+                    raise koji.BuildError("package %s not in list for tag %s" \
+                        % (build_info['name'], dest_tag['name']))
                 elif dest_cfg['blocked']:
-                    raise koji.BuildError, "package %s is blocked for tag %s" \
-                        % (build_info['name'], dest_tag['name'])
+                    raise koji.BuildError("package %s is blocked for tag %s" \
+                        % (build_info['name'], dest_tag['name']))
 
             build_info = self.session.host.initMavenBuild(self.id, build_info, maven_info)
             self.build_id = build_info['id']
@@ -1359,7 +1359,7 @@ class BuildMavenTask(BaseBuildTask):
 
         repo_id = opts.get('repo_id')
         if not repo_id:
-            raise koji.BuildError, 'A repo_id must be provided'
+            raise koji.BuildError('A repo_id must be provided')
         repo_info = self.session.repoInfo(repo_id, strict=True)
         event_id = repo_info['create_event']
 
@@ -1388,10 +1388,10 @@ class BuildMavenTask(BaseBuildTask):
             self.session.host.updateBuildRootList(buildroot.id, buildroot.getPackageList())
             if rv:
                 buildroot.expire()
-                raise koji.BuildrootError, 'error installing packages, %s' % buildroot._mockResult(rv, logfile='mock_output.log')
+                raise koji.BuildrootError('error installing packages, %s' % buildroot._mockResult(rv, logfile='mock_output.log'))
 
         if not os.path.exists('%s/usr/bin/mvn' % buildroot.rootdir()):
-            raise koji.BuildError, '/usr/bin/mvn was not found in the buildroot'
+            raise koji.BuildError('/usr/bin/mvn was not found in the buildroot')
 
         scmdir = '%s/maven/build' % buildroot.rootdir()
         outputdir = '%s/maven/output' % buildroot.rootdir()
@@ -1430,13 +1430,13 @@ class BuildMavenTask(BaseBuildTask):
                            os.path.isfile(os.path.join(patchcheckoutdir, patch)) and \
                            patch.endswith('.patch')]
             if not patches:
-                raise koji.BuildError, 'no patches found at %s' % self.opts.get('patches')
+                raise koji.BuildError('no patches found at %s' % self.opts.get('patches'))
             patches.sort()
             for patch in patches:
                 cmd = ['/usr/bin/patch', '--verbose', '--no-backup-if-mismatch', '-d', sourcedir, '-p1', '-i', os.path.join(patchcheckoutdir, patch)]
                 ret = log_output(self.session, cmd[0], cmd, patchlog, uploadpath, logerror=1, append=1)
                 if ret:
-                    raise koji.BuildError, 'error applying patches from %s, see patches.log for details' % self.opts.get('patches')
+                    raise koji.BuildError('error applying patches from %s, see patches.log for details' % self.opts.get('patches'))
 
         # Set ownership of the entire source tree to the mock user
         uid = pwd.getpwnam(self.options.mockuser)[2]
@@ -1458,7 +1458,7 @@ class BuildMavenTask(BaseBuildTask):
                     pomfile = maven_options[i + 1]
                     break
                 else:
-                    raise koji.BuildError, '%s option requires a file path' % opt
+                    raise koji.BuildError('%s option requires a file path' % opt)
             elif opt.startswith('-f=') or opt.startswith('--file='):
                 pomfile = opt.split('=', 1)[1]
                 break
@@ -1472,7 +1472,7 @@ class BuildMavenTask(BaseBuildTask):
 
         build_pom = os.path.join(sourcedir, pomfile)
         if not os.path.exists(build_pom):
-            raise koji.BuildError, '%s does not exist' % pomfile
+            raise koji.BuildError('%s does not exist' % pomfile)
         pom_info = koji.parse_pom(build_pom)
         maven_info = koji.pom_to_maven_info(pom_info)
 
@@ -1546,10 +1546,10 @@ class WrapperRPMTask(BaseBuildTask):
         spec = open(filename).read()
         for tag in ("Packager", "Distribution", "Vendor"):
             if re.match("%s:" % tag, spec, re.M):
-                raise koji.BuildError, "%s is not allowed to be set in spec file" % tag
+                raise koji.BuildError("%s is not allowed to be set in spec file" % tag)
         for tag in ("packager", "distribution", "vendor"):
             if re.match("%%define\s+%s\s+" % tag, spec, re.M):
-                raise koji.BuildError, "%s is not allowed to be defined in spec file" % tag
+                raise koji.BuildError("%s is not allowed to be defined in spec file" % tag)
 
     def checkHost(self, hostdata):
         target = self.params[1]
@@ -1560,7 +1560,7 @@ class WrapperRPMTask(BaseBuildTask):
             opts = {}
 
         if not (build or task):
-            raise koji.BuildError, 'build and/or task must be specified'
+            raise koji.BuildError('build and/or task must be specified')
 
         values = {}
 
@@ -1604,7 +1604,7 @@ class WrapperRPMTask(BaseBuildTask):
             # called as a top-level task to create wrapper rpms for an existing build
             # verify that the build is complete
             if not build['state'] == koji.BUILD_STATES['COMPLETE']:
-                raise koji.BuildError, 'cannot call wrapperRPM on a build that did not complete successfully'
+                raise koji.BuildError('cannot call wrapperRPM on a build that did not complete successfully')
 
             # get the list of files from the build instead of the task, because the task output directory may
             # have already been cleaned up
@@ -1615,7 +1615,7 @@ class WrapperRPMTask(BaseBuildTask):
             elif image_info:
                 build_artifacts = self.session.listArchives(buildID=build['id'], type='image')
             else:
-                raise koji.BuildError, 'unsupported build type'
+                raise koji.BuildError('unsupported build type')
 
             for artifact in build_artifacts:
                 artifact_name = artifact['filename']
@@ -1645,7 +1645,7 @@ class WrapperRPMTask(BaseBuildTask):
                     assert False  # pragma: no cover
 
         if not artifacts:
-            raise koji.BuildError, 'no output found for %s' % (task and koji.taskLabel(task) or koji.buildLabel(build))
+            raise koji.BuildError('no output found for %s' % (task and koji.taskLabel(task) or koji.buildLabel(build)))
 
         values['artifacts'] = artifacts
         values['all_artifacts'] = all_artifacts
@@ -1684,7 +1684,7 @@ class WrapperRPMTask(BaseBuildTask):
 
         repo_id = opts.get('repo_id')
         if not repo_id:
-            raise koji.BuildError, "A repo id must be provided"
+            raise koji.BuildError("A repo id must be provided")
 
         repo_info = self.session.repoInfo(repo_id, strict=True)
         event_id = repo_info['create_event']
@@ -1709,7 +1709,7 @@ class WrapperRPMTask(BaseBuildTask):
                     spec_template = os.path.join(path, filename)
                     break
         if not spec_template:
-            raise koji.BuildError, 'no spec file template found at URL: %s' % spec_url
+            raise koji.BuildError('no spec file template found at URL: %s' % spec_url)
 
         # Put the jars into the same directory as the specfile.  This directory will be
         # set to the rpm _sourcedir so other files in the SCM may be referenced in the
@@ -1752,9 +1752,9 @@ class WrapperRPMTask(BaseBuildTask):
 
         srpms = glob.glob('%s/*.src.rpm' % buildroot.resultdir())
         if len(srpms) == 0:
-            raise koji.BuildError, 'no srpms found in %s' % buildroot.resultdir()
+            raise koji.BuildError('no srpms found in %s' % buildroot.resultdir())
         elif len(srpms) > 1:
-            raise koji.BuildError, 'multiple srpms found in %s: %s' % (buildroot.resultdir(), ', '.join(srpms))
+            raise koji.BuildError('multiple srpms found in %s: %s' % (buildroot.resultdir(), ', '.join(srpms)))
         else:
             srpm = srpms[0]
 
@@ -1771,11 +1771,11 @@ class WrapperRPMTask(BaseBuildTask):
             if not opts.get('skip_tag'):
                 # Make sure package is on the list for this tag
                 if pkg_cfg is None:
-                    raise koji.BuildError, "package %s not in list for tag %s" \
-                          % (data['name'], build_target['dest_tag_name'])
+                    raise koji.BuildError("package %s not in list for tag %s" \
+                          % (data['name'], build_target['dest_tag_name']))
                 elif pkg_cfg['blocked']:
-                    raise koji.BuildError, "package %s is blocked for tag %s" \
-                          % (data['name'], build_target['dest_tag_name'])
+                    raise koji.BuildError("package %s is blocked for tag %s" \
+                          % (data['name'], build_target['dest_tag_name']))
             self.new_build_id = self.session.host.initBuild(data)
 
         try:
@@ -1799,8 +1799,8 @@ class WrapperRPMTask(BaseBuildTask):
                 else:
                     if self.new_build_id:
                         self.session.host.failBuild(self.id, self.new_build_id)
-                    raise koji.BuildError, 'multiple srpms found in %s: %s, %s' % \
-                        (resultdir, srpm, filename)
+                    raise koji.BuildError('multiple srpms found in %s: %s, %s' % \
+                        (resultdir, srpm, filename))
             elif filename.endswith('.rpm'):
                 rpms.append(filename)
             elif filename.endswith('.log'):
@@ -1808,18 +1808,18 @@ class WrapperRPMTask(BaseBuildTask):
             else:
                 if self.new_build_id:
                     self.session.host.failBuild(self.id, self.new_build_id)
-                raise koji.BuildError, 'unexpected file found in %s: %s' % \
-                    (resultdir, filename)
+                raise koji.BuildError('unexpected file found in %s: %s' % \
+                    (resultdir, filename))
 
         if not srpm:
             if self.new_build_id:
                 self.session.host.failBuild(self.id, self.new_build_id)
-            raise koji.BuildError, 'no srpm found'
+            raise koji.BuildError('no srpm found')
 
         if not rpms:
             if self.new_build_id:
                 self.session.host.failBuild(self.id, self.new_build_id)
-            raise koji.BuildError, 'no rpms found'
+            raise koji.BuildError('no rpms found')
 
         try:
             for rpm in [srpm] + rpms:
@@ -1884,7 +1884,7 @@ class ChainMavenTask(MultiPlatformTask):
             opts = {}
         target_info = self.session.getBuildTarget(target)
         if not target_info:
-            raise koji.BuildError, 'unknown build target: %s' % target
+            raise koji.BuildError('unknown build target: %s' % target)
         dest_tag = self.session.getTag(target_info['dest_tag'], strict=True)
 
         if not (opts.get('scratch') or opts.get('skip_tag')):
@@ -1892,11 +1892,11 @@ class ChainMavenTask(MultiPlatformTask):
                 dest_cfg = self.session.getPackageConfig(dest_tag['id'], package)
                 # Make sure package is on the list for this tag
                 if dest_cfg is None:
-                    raise koji.BuildError, "package %s not in list for tag %s" \
-                        % (package, dest_tag['name'])
+                    raise koji.BuildError("package %s not in list for tag %s" \
+                        % (package, dest_tag['name']))
                 elif dest_cfg['blocked']:
-                    raise koji.BuildError, "package %s is blocked for tag %s" \
-                        % (package, dest_tag['name'])
+                    raise koji.BuildError("package %s is blocked for tag %s" \
+                        % (package, dest_tag['name']))
 
         self.depmap = {}
         for package, params in builds.items():
@@ -1958,7 +1958,7 @@ class ChainMavenTask(MultiPlatformTask):
                                                           task_opts],
                                            label=package)
                 else:
-                    raise koji.BuildError, 'unsupported build type: %s' % buildtype
+                    raise koji.BuildError('unsupported build type: %s' % buildtype)
 
                 running[task_id] = package
                 del todo[package]
@@ -1986,13 +1986,13 @@ class ChainMavenTask(MultiPlatformTask):
                                 self.done[package] = child['id']
                                 break
                         else:
-                            raise koji.BuildError, 'could not find buildMaven subtask of %s' % task_id
+                            raise koji.BuildError('could not find buildMaven subtask of %s' % task_id)
                     self.results.append('%s built from %s by task %s' % \
                                         (package, task_url, task_id))
                 else:
                     task_builds = self.session.listBuilds(taskID=task_id)
                     if not task_builds:
-                        raise koji.BuildError, 'could not find build for task %s' % task_id
+                        raise koji.BuildError('could not find build for task %s' % task_id)
                     task_build = task_builds[0]
                     self.done[package] = task_build['nvr']
                     self.results.append('%s built from %s' % (task_build['nvr'], task_url))
@@ -2001,7 +2001,7 @@ class ChainMavenTask(MultiPlatformTask):
 
         if todo:
             # should never happen, the client should have checked for circular dependencies
-            raise koji.BuildError, 'unable to run chain build, circular dependencies'
+            raise koji.BuildError('unable to run chain build, circular dependencies')
         return self.results
 
     def depset(self, package):
@@ -2116,9 +2116,9 @@ class BuildImageTask(MultiPlatformTask):
         if not opts.get('skip_tag') and not opts.get('scratch'):
             # Make sure package is on the list for this tag
             if pkg_cfg is None:
-                raise koji.BuildError, "package (image) %s not in list for tag %s" % (name, target_info['dest_tag_name'])
+                raise koji.BuildError("package (image) %s not in list for tag %s" % (name, target_info['dest_tag_name']))
             elif pkg_cfg['blocked']:
-                raise koji.BuildError, "package (image)  %s is blocked for tag %s" % (name, target_info['dest_tag_name'])
+                raise koji.BuildError("package (image)  %s is blocked for tag %s" % (name, target_info['dest_tag_name']))
         return self.session.host.initImageBuild(self.id,
             dict(name=name, version=version, release=release, epoch=0))
 
@@ -2137,18 +2137,18 @@ class BuildBaseImageTask(BuildImageTask):
         #check requested arches against build tag
         buildconfig = self.session.getBuildConfig(build_tag)
         if not buildconfig['arches']:
-            raise koji.BuildError, "No arches for tag %(name)s [%(id)s]" % buildconfig
+            raise koji.BuildError("No arches for tag %(name)s [%(id)s]" % buildconfig)
         tag_archlist = [koji.canonArch(a) for a in buildconfig['arches'].split()]
         for arch in arches:
             if koji.canonArch(arch) not in tag_archlist:
-                raise koji.BuildError, "Invalid arch for build tag: %s" % arch
+                raise koji.BuildError("Invalid arch for build tag: %s" % arch)
 
         if not opts:
             opts = {}
 
         if not ozif_enabled:
             self.logger.error("ImageFactory features require the following dependencies: pykickstart, imagefactory, oz and possibly python-hashlib")
-            raise koji.ApplianceError, 'ImageFactory functions not available'
+            raise koji.ApplianceError('ImageFactory functions not available')
 
         # build image(s)
         bld_info = None
@@ -2272,10 +2272,10 @@ class BuildApplianceTask(BuildImageTask):
         #check requested arch against build tag
         buildconfig = self.session.getBuildConfig(build_tag)
         if not buildconfig['arches']:
-            raise koji.BuildError, "No arches for tag %(name)s [%(id)s]" % buildconfig
+            raise koji.BuildError("No arches for tag %(name)s [%(id)s]" % buildconfig)
         tag_archlist = [koji.canonArch(a) for a in buildconfig['arches'].split()]
         if koji.canonArch(arch) not in tag_archlist:
-            raise koji.BuildError, "Invalid arch for build tag: %s" % arch
+            raise koji.BuildError("Invalid arch for build tag: %s" % arch)
 
 
         if not opts:
@@ -2283,7 +2283,7 @@ class BuildApplianceTask(BuildImageTask):
 
         if not image_enabled:
             self.logger.error("Appliance features require the following dependencies: pykickstart, and possibly python-hashlib")
-            raise koji.ApplianceError, 'Appliance functions not available'
+            raise koji.ApplianceError('Appliance functions not available')
 
         # build image
         bld_info = None
@@ -2358,17 +2358,17 @@ class BuildLiveCDTask(BuildImageTask):
         #check requested arch against build tag
         buildconfig = self.session.getBuildConfig(build_tag)
         if not buildconfig['arches']:
-            raise koji.BuildError, "No arches for tag %(name)s [%(id)s]" % buildconfig
+            raise koji.BuildError("No arches for tag %(name)s [%(id)s]" % buildconfig)
         tag_archlist = [koji.canonArch(a) for a in buildconfig['arches'].split()]
         if koji.canonArch(arch) not in tag_archlist:
-            raise koji.BuildError, "Invalid arch for build tag: %s" % arch
+            raise koji.BuildError("Invalid arch for build tag: %s" % arch)
 
         if not opts:
             opts = {}
         if not image_enabled:
             self.logger.error("LiveCD features require the following dependencies: "
                     "pykickstart, pycdio, and possibly python-hashlib")
-            raise koji.LiveCDError, 'LiveCD functions not available'
+            raise koji.LiveCDError('LiveCD functions not available')
 
         # build the image
         bld_info = None
@@ -2445,14 +2445,14 @@ class BuildLiveMediaTask(BuildImageTask):
         #check requested arch against build tag
         buildconfig = self.session.getBuildConfig(build_tag)
         if not buildconfig['arches']:
-            raise koji.BuildError, "No arches for tag %(name)s [%(id)s]" % buildconfig
+            raise koji.BuildError("No arches for tag %(name)s [%(id)s]" % buildconfig)
         tag_archlist = [koji.canonArch(a) for a in buildconfig['arches'].split()]
 
         # check arches and remove duplicates
         arches = set(arches)
         for arch in arches:
             if koji.canonArch(arch) not in tag_archlist:
-                raise koji.BuildError, "Invalid arch for build tag: %s" % arch
+                raise koji.BuildError("Invalid arch for build tag: %s" % arch)
 
         if not opts:
             opts = {}
@@ -2720,7 +2720,7 @@ class ImageTask(BaseTaskHandler):
 
         # put the new ksfile in the output directory
         if not os.path.exists(kojikspath):
-            raise koji.LiveCDError, "KS file missing: %s" % kojikspath
+            raise koji.LiveCDError("KS file missing: %s" % kojikspath)
         self.uploadFile(kojikspath)
         return kskoji # absolute path within chroot
 
@@ -2744,7 +2744,7 @@ class ImageTask(BaseTaskHandler):
                     hdrlist.append(hdr)
                     found = True
         if not found:
-            raise koji.LiveCDError, 'No repos found in yum cache!'
+            raise koji.LiveCDError('No repos found in yum cache!')
         return hdrlist
 
     def _shortenVolID(self, name, version, release):
@@ -2795,7 +2795,7 @@ class ApplianceTask(ImageTask):
         for part in self.ks.handler.partition.partitions:
             if part.mountpoint == '/':
                 return part.disk
-        raise koji.ApplianceError, 'kickstart lacks a "/" mountpoint'
+        raise koji.ApplianceError('kickstart lacks a "/" mountpoint')
 
     def handler(self, name, version, release, arch, target_info, build_tag, repo_info, ksfile, opts=None):
 
@@ -2829,8 +2829,8 @@ class ApplianceTask(ImageTask):
         rv = broot.mock(['--cwd', '/tmp', '--chroot', '--'] + cmd)
         self.uploadFile(os.path.join(broot.rootdir(), app_log[1:]))
         if rv:
-            raise koji.ApplianceError, \
-                "Could not create appliance: %s" % parseStatus(rv, 'appliance-creator') + "; see root.log or appliance.log for more information"
+            raise koji.ApplianceError(
+                "Could not create appliance: %s" % parseStatus(rv, 'appliance-creator') + "; see root.log or appliance.log for more information")
 
         # Find the results
         results = []
@@ -2840,7 +2840,7 @@ class ApplianceTask(ImageTask):
                                             directory, f))
         self.logger.debug('output: %s' % results)
         if len(results) == 0:
-            raise koji.ApplianceError, "Could not find image build results!"
+            raise koji.ApplianceError("Could not find image build results!")
         imgdata = {
             'arch': arch,
             'rootdev': self.getRootDevice(),
@@ -2884,12 +2884,12 @@ class LiveCDTask(ImageTask):
         """
         fd = open(manifile, 'w')
         if not fd:
-            raise koji.GenericError, \
-                'Unable to open manifest file (%s) for writing!' % manifile
+            raise koji.GenericError(
+                'Unable to open manifest file (%s) for writing!' % manifile)
         iso = iso9660.ISO9660.IFS(source=image)
         if not iso.is_open():
-            raise koji.GenericError, \
-                'Could not open %s as an ISO-9660 image!' % image
+            raise koji.GenericError(
+                'Could not open %s as an ISO-9660 image!' % image)
 
         # image metadata
         id = iso.get_application_id()
@@ -2968,8 +2968,8 @@ class LiveCDTask(ImageTask):
         rv = broot.mock(['--cwd', '/tmp', '--chroot', '--'] + cmd)
         self.uploadFile(os.path.join(broot.rootdir(), livecd_log[1:]))
         if rv:
-            raise koji.LiveCDError, \
-                'Could not create LiveCD: %s' % parseStatus(rv, 'livecd-creator') + '; see root.log or livecd.log for more information'
+            raise koji.LiveCDError(
+                'Could not create LiveCD: %s' % parseStatus(rv, 'livecd-creator') + '; see root.log or livecd.log for more information')
 
         # Find the resultant iso
         # The cwd of the livecd-creator process is /tmp in the chroot, so
@@ -2981,9 +2981,9 @@ class LiveCDTask(ImageTask):
                 if not isofile:
                     isofile = afile
                 else:
-                    raise koji.LiveCDError, 'multiple .iso files found: %s and %s' % (isofile, afile)
+                    raise koji.LiveCDError('multiple .iso files found: %s and %s' % (isofile, afile))
         if not isofile:
-            raise koji.LiveCDError, 'could not find iso file in chroot'
+            raise koji.LiveCDError('could not find iso file in chroot')
         isosrc = os.path.join(broot.rootdir(), 'tmp', isofile)
 
         # copy the iso out of the chroot. If we were given an isoname,
@@ -3035,12 +3035,12 @@ class LiveMediaTask(ImageTask):
         """
         fd = open(manifile, 'w')
         if not fd:
-            raise koji.GenericError, \
-                'Unable to open manifest file (%s) for writing!' % manifile
+            raise koji.GenericError(
+                'Unable to open manifest file (%s) for writing!' % manifile)
         iso = iso9660.ISO9660.IFS(source=image)
         if not iso.is_open():
-            raise koji.GenericError, \
-                'Could not open %s as an ISO-9660 image!' % image
+            raise koji.GenericError(
+                'Could not open %s as an ISO-9660 image!' % image)
 
         # image metadata
         id = iso.get_application_id()
@@ -3167,8 +3167,8 @@ class LiveMediaTask(ImageTask):
                 self.uploadFile(os.path.join(filepath))
 
         if rv:
-            raise koji.LiveMediaError, \
-                'Could not create LiveMedia: %s' % parseStatus(rv, 'livemedia-creator') + '; see root.log or livemedia-out.log for more information'
+            raise koji.LiveMediaError(
+                'Could not create LiveMedia: %s' % parseStatus(rv, 'livemedia-creator') + '; see root.log or livemedia-out.log for more information')
 
         # Find the resultant iso
         # The cwd of the livemedia-creator process is /tmp in the chroot, so
@@ -3181,9 +3181,9 @@ class LiveMediaTask(ImageTask):
                 if not isofile:
                     isofile = afile
                 else:
-                    raise koji.LiveMediaError, 'multiple .iso files found: %s and %s' % (isofile, afile)
+                    raise koji.LiveMediaError('multiple .iso files found: %s and %s' % (isofile, afile))
         if not isofile:
-            raise koji.LiveMediaError, 'could not find iso file in chroot'
+            raise koji.LiveMediaError('could not find iso file in chroot')
         isosrc = os.path.join(rootresultsdir, isofile)
 
 
@@ -3344,7 +3344,7 @@ class OzImageTask(BaseTaskHandler):
 
         # put the new ksfile in the output directory
         if not os.path.exists(kspath):
-            raise koji.BuildError, "KS file missing: %s" % kspath
+            raise koji.BuildError("KS file missing: %s" % kspath)
         self.uploadFile(kspath) # upload the modified ks file
         return kspath
 
@@ -4016,9 +4016,9 @@ class BuildIndirectionImageTask(OzImageTask):
         if not opts.get('skip_tag') and not opts.get('scratch'):
             # Make sure package is on the list for this tag
             if pkg_cfg is None:
-                raise koji.BuildError, "package (image) %s not in list for tag %s" % (name, target_info['dest_tag_name'])
+                raise koji.BuildError("package (image) %s not in list for tag %s" % (name, target_info['dest_tag_name']))
             elif pkg_cfg['blocked']:
-                raise koji.BuildError, "package (image)  %s is blocked for tag %s" % (name, target_info['dest_tag_name'])
+                raise koji.BuildError("package (image)  %s is blocked for tag %s" % (name, target_info['dest_tag_name']))
         return self.session.host.initImageBuild(self.id,
             dict(name=name, version=version, release=release, epoch=0))
 
@@ -4344,10 +4344,10 @@ class BuildSRPMFromSCMTask(BaseBuildTask):
         spec = open(filename).read()
         for tag in ("Packager", "Distribution", "Vendor"):
             if re.match("%s:" % tag, spec, re.M):
-                raise koji.BuildError, "%s is not allowed to be set in spec file" % tag
+                raise koji.BuildError("%s is not allowed to be set in spec file" % tag)
         for tag in ("packager", "distribution", "vendor"):
             if re.match("%%define\s+%s\s+" % tag, spec, re.M):
-                raise koji.BuildError, "%s is not allowed to be defined in spec file" % tag
+                raise koji.BuildError("%s is not allowed to be defined in spec file" % tag)
 
     def patch_scm_source(self, sourcedir, logfile, opts):
         # override if desired
@@ -4366,7 +4366,7 @@ class BuildSRPMFromSCMTask(BaseBuildTask):
             opts = {}
         repo_id = opts.get('repo_id')
         if not repo_id:
-            raise koji.BuildError, "A repo id must be provided"
+            raise koji.BuildError("A repo id must be provided")
 
         repo_info = self.session.repoInfo(repo_id, strict=True)
         event_id = repo_info['create_event']
@@ -4425,9 +4425,9 @@ class BuildSRPMFromSCMTask(BaseBuildTask):
 
         srpms = glob.glob('%s/*.src.rpm' % broot.resultdir())
         if len(srpms) == 0:
-            raise koji.BuildError, "No srpms found in %s" % sourcedir
+            raise koji.BuildError("No srpms found in %s" % sourcedir)
         elif len(srpms) > 1:
-            raise koji.BuildError, "Multiple srpms found in %s: %s" % (sourcedir, ", ".join(srpms))
+            raise koji.BuildError("Multiple srpms found in %s: %s" % (sourcedir, ", ".join(srpms)))
         else:
             srpm = srpms[0]
 
@@ -4438,7 +4438,7 @@ class BuildSRPMFromSCMTask(BaseBuildTask):
         release = h[rpm.RPMTAG_RELEASE]
         srpm_name = "%(name)s-%(version)s-%(release)s.src.rpm" % locals()
         if srpm_name != os.path.basename(srpm):
-            raise koji.BuildError, 'srpm name mismatch: %s != %s' % (srpm_name, os.path.basename(srpm))
+            raise koji.BuildError('srpm name mismatch: %s != %s' % (srpm_name, os.path.basename(srpm)))
 
         #upload srpm and return
         self.uploadFile(srpm)
@@ -4759,7 +4759,7 @@ class NewRepoTask(BaseTaskHandler):
         repo_id, event_id = self.session.host.repoInit(tinfo['id'], **kwargs)
         path = koji.pathinfo.repo(repo_id, tinfo['name'])
         if not os.path.isdir(path):
-            raise koji.GenericError, "Repo directory missing: %s" % path
+            raise koji.GenericError("Repo directory missing: %s" % path)
         arches = []
         for fn in os.listdir(path):
             if fn != 'groups' and os.path.isfile("%s/%s/pkglist" % (path, fn)):
@@ -4805,13 +4805,13 @@ class CreaterepoTask(BaseTaskHandler):
         #arch is the arch of the repo, not the task
         rinfo = self.session.repoInfo(repo_id, strict=True)
         if rinfo['state'] != koji.REPO_INIT:
-            raise koji.GenericError, "Repo %(id)s not in INIT state (got %(state)s)" % rinfo
+            raise koji.GenericError("Repo %(id)s not in INIT state (got %(state)s)" % rinfo)
         self.repo_id = rinfo['id']
         self.pathinfo = koji.PathInfo(self.options.topdir)
         toprepodir = self.pathinfo.repo(repo_id, rinfo['tag_name'])
         self.repodir = '%s/%s' % (toprepodir, arch)
         if not os.path.isdir(self.repodir):
-            raise koji.GenericError, "Repo directory missing: %s" % self.repodir
+            raise koji.GenericError("Repo directory missing: %s" % self.repodir)
         groupdata = os.path.join(toprepodir, 'groups', 'comps.xml')
         #set up our output dir
         self.outdir = '%s/repo' % self.workdir
@@ -4875,8 +4875,8 @@ class CreaterepoTask(BaseTaskHandler):
         logfile = '%s/createrepo.log' % self.workdir
         status = log_output(self.session, cmd[0], cmd, logfile, self.getUploadDir(), logerror=True)
         if not isSuccess(status):
-            raise koji.GenericError, 'failed to create repo: %s' \
-                    % parseStatus(status, ' '.join(cmd))
+            raise koji.GenericError('failed to create repo: %s' \
+                    % parseStatus(status, ' '.join(cmd)))
 
     def merge_repos(self, external_repos, arch, groupdata):
         repos = []
@@ -4906,8 +4906,8 @@ class CreaterepoTask(BaseTaskHandler):
         logfile = '%s/mergerepos.log' % self.workdir
         status = log_output(self.session, cmd[0], cmd, logfile, self.getUploadDir(), logerror=True)
         if not isSuccess(status):
-            raise koji.GenericError, 'failed to merge repos: %s' \
-                % parseStatus(status, ' '.join(cmd))
+            raise koji.GenericError('failed to merge repos: %s' \
+                % parseStatus(status, ' '.join(cmd)))
 
 class WaitrepoTask(BaseTaskHandler):
 
@@ -4941,10 +4941,10 @@ class WaitrepoTask(BaseTaskHandler):
         if isinstance(newer_than, basestring) and newer_than.lower() == "now":
             newer_than = start
         if not isinstance(newer_than, (type(None), int, long, float)):
-            raise koji.GenericError, "Invalid value for newer_than: %s" % newer_than
+            raise koji.GenericError("Invalid value for newer_than: %s" % newer_than)
 
         if newer_than and nvrs:
-            raise koji.GenericError, "only one of (newer_than, nvrs) may be specified"
+            raise koji.GenericError("only one of (newer_than, nvrs) may be specified")
 
         if not nvrs:
             nvrs = []
@@ -4971,11 +4971,11 @@ class WaitrepoTask(BaseTaskHandler):
 
             if (time.time() - start) > (self.TIMEOUT * 60.0):
                 if builds:
-                    raise koji.GenericError, "Unsuccessfully waited %s for %s to appear in the %s repo" % \
-                        (koji.util.duration(start), koji.util.printList(nvrs), taginfo['name'])
+                    raise koji.GenericError("Unsuccessfully waited %s for %s to appear in the %s repo" % \
+                        (koji.util.duration(start), koji.util.printList(nvrs), taginfo['name']))
                 else:
-                    raise koji.GenericError, "Unsuccessfully waited %s for a new %s repo" % \
-                        (koji.util.duration(start), taginfo['name'])
+                    raise koji.GenericError("Unsuccessfully waited %s for a new %s repo" % \
+                        (koji.util.duration(start), taginfo['name']))
 
             time.sleep(self.PAUSE)
             last_repo = repo
diff --git a/cli/koji b/cli/koji
index 78242fd..20a950e 100755
--- a/cli/koji
+++ b/cli/koji
@@ -588,11 +588,11 @@ def handle_assign_task(options, session, args):
 
     taskinfo = session.getTaskInfo(task_id, request=False)
     if taskinfo is None:
-        raise koji.GenericError, "No such task: %s" % task_id
+        raise koji.GenericError("No such task: %s" % task_id)
 
     hostinfo = session.getHost(hostname)
     if hostinfo is None:
-        raise koji.GenericError, "No such host: %s" % hostname
+        raise koji.GenericError("No such host: %s" % hostname)
 
     force = False
     if options.force:
@@ -2021,7 +2021,7 @@ def handle_prune_signed_copies(options, session, args):
             if x['active']:
                 fmt += " [still active]"
         else:
-            raise koji.GenericError, "unknown event: (%r, %r)" % (event_id, x)
+            raise koji.GenericError("unknown event: (%r, %r)" % (event_id, x))
         time_str = time.asctime(time.localtime(ts))
         return "%s: %s" % (time_str, fmt % x)
     for nvr, binfo in builds:
@@ -2062,7 +2062,7 @@ def handle_prune_signed_copies(options, session, args):
             hist = session.tagHistory(tag=tag_name, package=binfo['name'])
             if not hist:
                 #really shouldn't happen
-                raise koji.GenericError, "No history found for %s in %s" % (nvr, tag_name)
+                raise koji.GenericError("No history found for %s in %s" % (nvr, tag_name))
             timeline = []
             for x in hist:
                 #note that for revoked entries, we're effectively splitting them into
@@ -2082,7 +2082,7 @@ def handle_prune_signed_copies(options, session, args):
                     break
             if not latest_ts:
                 #really shouldn't happen
-                raise koji.GenericError, "No creation event found for %s in %s" % (nvr, tag_name)
+                raise koji.GenericError("No creation event found for %s in %s" % (nvr, tag_name))
             our_entry = entry
             if options.debug:
                 print(_histline(event_id, our_entry))
@@ -2130,8 +2130,8 @@ def handle_prune_signed_copies(options, session, args):
                 if entry['build_id'] == binfo['id']:
                     if is_create:
                         #shouldn't happen
-                        raise koji.GenericError, "Duplicate creation event found for %s in %s" \
-                                                    % (nvr, tag_name)
+                        raise koji.GenericError("Duplicate creation event found for %s in %s" \
+                                                    % (nvr, tag_name))
                     else:
                         #we've been revoked
                         revoke_ts = entry['revoke_ts']
@@ -4127,7 +4127,7 @@ def anon_handle_list_tag_history(options, session, args):
             if x['active']:
                 fmt += " [still active]"
         else:
-            raise koji.GenericError, "unknown event: (%r, %r)" % (event_id, x)
+            raise koji.GenericError("unknown event: (%r, %r)" % (event_id, x))
         time_str = time.asctime(time.localtime(ts))
         return "%s: %s" % (time_str, fmt % x)
     for event_id, x in timeline:
@@ -4654,7 +4654,7 @@ def _printTaskInfo(session, task_id, level=0, recurse=True, verbose=True):
     info = session.getTaskInfo(task_id)
 
     if info is None:
-        raise koji.GenericError, "No such task: %d" % task_id
+        raise koji.GenericError("No such task: %d" % task_id)
 
     if info['host_id']:
         host_info = session.getHost(info['host_id'])
@@ -5339,7 +5339,7 @@ def _parse_tagpri(tagpri):
         try:
             pri = int(parts[1])
         except ValueError:
-            raise koji.GenericError, "Invalid priority: %s" % parts[1]
+            raise koji.GenericError("Invalid priority: %s" % parts[1])
         return tag, pri
 
 def handle_add_external_repo(options, session, args):
@@ -5648,11 +5648,11 @@ def _build_image_indirection(options, task_opts, session, args):
     # Do some sanity checks before even attempting to create the session
     if not (bool(task_opts.utility_image_task) !=
             bool(task_opts.utility_image_build)):
-        raise koji.GenericError, _("You must specify either a utility-image task or build ID/NVR")
+        raise koji.GenericError(_("You must specify either a utility-image task or build ID/NVR"))
 
     if not (bool(task_opts.base_image_task) !=
             bool(task_opts.base_image_build)):
-        raise koji.GenericError, _("You must specify either a base-image task or build ID/NVR")
+        raise koji.GenericError(_("You must specify either a base-image task or build ID/NVR"))
 
     required_opts = [ 'name', 'version', 'arch', 'target', 'indirection_template', 'results_loc' ]
     optional_opts = [ 'indirection_template_url', 'scratch', 'utility_image_task', 'utility_image_build',
@@ -5665,7 +5665,7 @@ def _build_image_indirection(options, task_opts, session, args):
 
     if len(missing) > 0:
         print("Missing the following required options: %s" % ' '.join(['--%s' % o.replace('_','-') for o in missing]))
-        raise koji.GenericError, _("Missing required options specified above")
+        raise koji.GenericError(_("Missing required options specified above"))
 
     activate_session(session)
 
@@ -5685,11 +5685,11 @@ def _build_image_indirection(options, task_opts, session, args):
 
     tmp_target = session.getBuildTarget(task_opts.target)
     if not tmp_target:
-        raise koji.GenericError, _("Unknown build target: %s" % tmp_target)
+        raise koji.GenericError(_("Unknown build target: %s" % tmp_target))
     dest_tag = session.getTag(tmp_target['dest_tag'])
     if not dest_tag:
-        raise koji.GenericError, _("Unknown destination tag: %s" %
-                                   tmp_target['dest_tag_name'])
+        raise koji.GenericError(_("Unknown destination tag: %s" %
+                                   tmp_target['dest_tag_name']))
 
     # Set the architecture
     task_opts.arch = koji.canonArch(task_opts.arch)
@@ -5700,7 +5700,7 @@ def _build_image_indirection(options, task_opts, session, args):
     if not task_opts.indirection_template_url:
         if not task_opts.scratch:
             # only scratch builds can omit indirection_template_url
-            raise koji.GenericError, _("Non-scratch builds must provide a URL for the indirection template")
+            raise koji.GenericError(_("Non-scratch builds must provide a URL for the indirection template"))
         templatefile = task_opts.indirection_template
         serverdir = _unique_path('cli-image-indirection')
         session.uploadWrapper(templatefile, serverdir, callback=callback)
@@ -5861,7 +5861,7 @@ def _build_image(options, task_opts, session, args, img_type):
     """
 
     if img_type not in ('livecd', 'appliance', 'livemedia'):
-        raise koji.GenericError, 'Unrecognized image type: %s' % img_type
+        raise koji.GenericError('Unrecognized image type: %s' % img_type)
     activate_session(session)
 
     # Set the task's priority. Users can only lower it with --background.
@@ -5880,11 +5880,11 @@ def _build_image(options, task_opts, session, args, img_type):
     target = args[2]
     tmp_target = session.getBuildTarget(target)
     if not tmp_target:
-        raise koji.GenericError, _("Unknown build target: %s" % target)
+        raise koji.GenericError(_("Unknown build target: %s" % target))
     dest_tag = session.getTag(tmp_target['dest_tag'])
     if not dest_tag:
-        raise koji.GenericError, _("Unknown destination tag: %s" %
-                                   tmp_target['dest_tag_name'])
+        raise koji.GenericError(_("Unknown destination tag: %s" %
+                                   tmp_target['dest_tag_name']))
 
     # Set the architecture
     if img_type == 'livemedia':
@@ -5952,11 +5952,11 @@ def _build_image_oz(options, task_opts, session, args):
     target = args[2]
     tmp_target = session.getBuildTarget(target)
     if not tmp_target:
-        raise koji.GenericError, _("Unknown build target: %s" % target)
+        raise koji.GenericError(_("Unknown build target: %s" % target))
     dest_tag = session.getTag(tmp_target['dest_tag'])
     if not dest_tag:
-        raise koji.GenericError, _("Unknown destination tag: %s" %
-                                   tmp_target['dest_tag_name'])
+        raise koji.GenericError(_("Unknown destination tag: %s" %
+                                   tmp_target['dest_tag_name']))
 
     # Set the architectures
     arches = []
@@ -5968,7 +5968,7 @@ def _build_image_oz(options, task_opts, session, args):
     if not task_opts.ksurl:
         if not task_opts.scratch:
             # only scratch builds can omit ksurl
-            raise koji.GenericError, _("Non-scratch builds must provide ksurl")
+            raise koji.GenericError(_("Non-scratch builds must provide ksurl"))
         ksfile = task_opts.kickstart
         serverdir = _unique_path('cli-image')
         session.uploadWrapper(ksfile, serverdir, callback=callback)
diff --git a/hub/kojihub.py b/hub/kojihub.py
index 14a5338..7e1ff7c 100644
--- a/hub/kojihub.py
+++ b/hub/kojihub.py
@@ -109,13 +109,13 @@ class Task(object):
         q = """SELECT state,host_id FROM task WHERE id=%(task_id)s FOR UPDATE"""
         r = _fetchSingle(q, locals())
         if not r:
-            raise koji.GenericError, "No such task: %i" % task_id
+            raise koji.GenericError("No such task: %i" % task_id)
         state, otherhost = r
         return (state == koji.TASK_STATES['OPEN'] and otherhost == host_id)
 
     def assertHost(self, host_id):
         if not self.verifyHost(host_id):
-            raise koji.ActionNotAllowed, "host %d does not own task %d" % (host_id, self.id)
+            raise koji.ActionNotAllowed("host %d does not own task %d" % (host_id, self.id))
 
     def getOwner(self):
         """Return the owner (user_id) for this task"""
@@ -133,13 +133,13 @@ class Task(object):
         q = """SELECT owner FROM task WHERE id=%(task_id)s FOR UPDATE"""
         r = _fetchSingle(q, locals())
         if not r:
-            raise koji.GenericError, "No such task: %i" % task_id
+            raise koji.GenericError("No such task: %i" % task_id)
         (owner,) = r
         return (owner == user_id)
 
     def assertOwner(self, user_id=None):
         if not self.verifyOwner(user_id):
-            raise koji.ActionNotAllowed, "user %d does not own task %d" % (user_id, self.id)
+            raise koji.ActionNotAllowed("user %d does not own task %d" % (user_id, self.id))
 
     def lock(self, host_id, newstate='OPEN', force=False):
         """Attempt to associate the task for host, either to assign or open
@@ -155,7 +155,7 @@ class Task(object):
             q = """SELECT state,host_id FROM task WHERE id=%(task_id)i FOR UPDATE"""
             r = _fetchSingle(q, locals())
             if not r:
-                raise koji.GenericError, "No such task: %i" % task_id
+                raise koji.GenericError("No such task: %i" % task_id)
             state, otherhost = r
             if state == koji.TASK_STATES['FREE']:
                 if otherhost is not None:
@@ -223,11 +223,11 @@ class Task(object):
         query = """SELECT state FROM task WHERE id = %(id)i FOR UPDATE"""
         row = _fetchSingle(query, vars(self))
         if not row:
-            raise koji.GenericError, "No such task: %i" % self.id
+            raise koji.GenericError("No such task: %i" % self.id)
         oldstate = row[0]
         if koji.TASK_STATES[oldstate] in ['CLOSED', 'CANCELED', 'FAILED']:
-            raise koji.GenericError, "Cannot free task %i, state is %s" % \
-                    (self.id, koji.TASK_STATES[oldstate])
+            raise koji.GenericError("Cannot free task %i, state is %s" % \
+                    (self.id, koji.TASK_STATES[oldstate]))
         newstate = koji.TASK_STATES['FREE']
         newhost = None
         q = """UPDATE task SET state=%(newstate)s,host_id=%(newhost)s
@@ -365,12 +365,12 @@ class Task(object):
         parent = _singleValue(q, locals())
         if parent is not None:
             if strict:
-                raise koji.GenericError, "Task %d is not top-level (parent=%d)" % (task_id, parent)
+                raise koji.GenericError("Task %d is not top-level (parent=%d)" % (task_id, parent))
             #otherwise, find the top-level task and go from there
             seen = {task_id:1}
             while parent is not None:
                 if seen.has_key(parent):
-                    raise koji.GenericError, "Task LOOP at task %i" % task_id
+                    raise koji.GenericError("Task LOOP at task %i" % task_id)
                 task_id = parent
                 seen[task_id] = 1
                 parent = _singleValue(q, locals())
@@ -384,7 +384,7 @@ class Task(object):
         for task_id in tasklist:
             if seen.has_key(task_id):
                 #shouldn't happen
-                raise koji.GenericError, "Task LOOP at task %i" % task_id
+                raise koji.GenericError("Task LOOP at task %i" % task_id)
             seen[task_id] = 1
             Task(task_id).cancel(recurse=False)
             for (child_id,) in _fetchMulti(q_children, locals()):
@@ -404,12 +404,12 @@ class Task(object):
         query = """SELECT state,result FROM task WHERE id = %(id)i"""
         r = _fetchSingle(query, vars(self))
         if not r:
-            raise koji.GenericError, "No such task"
+            raise koji.GenericError("No such task")
         state, xml_result = r
         if koji.TASK_STATES[state] == 'CANCELED':
-            raise koji.GenericError, "Task %i is canceled" % self.id
+            raise koji.GenericError("Task %i is canceled" % self.id)
         elif koji.TASK_STATES[state] not in ['CLOSED', 'FAILED']:
-            raise koji.GenericError, "Task %i is not finished" % self.id
+            raise koji.GenericError("Task %i is not finished" % self.id)
         if xml_result.find('<?xml', 0, 10) == -1:
             #handle older base64 encoded data
             xml_result = base64.decodestring(xml_result)
@@ -461,7 +461,7 @@ class Task(object):
                 info['result'] = self.getResult()
             new_val = info[attr]
         else:
-            raise koji.GenericError, 'unknown callback type: %s' % cbtype
+            raise koji.GenericError('unknown callback type: %s' % cbtype)
         old_val = old_info[attr]
         if attr == 'state':
             # state is passed in as an integer, but we want to use the string
@@ -489,10 +489,10 @@ def make_task(method, arglist, **opts):
         q = """SELECT %s FROM task WHERE id = %%(parent)i""" % ','.join(fields)
         r = _fetchSingle(q, opts)
         if not r:
-            raise koji.GenericError, "Invalid parent task: %(parent)s" % opts
+            raise koji.GenericError("Invalid parent task: %(parent)s" % opts)
         pdata = dict(zip(fields, r))
         if pdata['state'] != koji.TASK_STATES['OPEN']:
-            raise koji.GenericError, "Parent task (id %(parent)s) is not open" % opts
+            raise koji.GenericError("Parent task (id %(parent)s) is not open" % opts)
         #default to a higher priority than parent
         opts.setdefault('priority', pdata['priority'] - 1)
         for f in ('owner', 'arch'):
@@ -503,7 +503,7 @@ def make_task(method, arglist, **opts):
         #calling function should enforce priority limitations, if applicable
         opts.setdefault('arch', 'noarch')
         if not context.session.logged_in:
-            raise koji.GenericError, 'task must have an owner'
+            raise koji.GenericError('task must have an owner')
         else:
             opts['owner'] = context.session.user_id
         opts['label'] = None
@@ -543,20 +543,20 @@ def make_task(method, arglist, **opts):
                 if not opts.get('parent'):
                     logger.error("Invalid channel policy result (no parent task): %s",
                                     ruleset.last_rule())
-                    raise koji.GenericError, "invalid channel policy"
+                    raise koji.GenericError("invalid channel policy")
                 opts['channel_id'] = pdata['channel_id']
             elif parts[0] == "req":
                 if 'channel' not in opts:
                     logger.error('Invalid channel policy result (no channel requested): %s',
                                     ruleset.last_rule())
-                    raise koji.GenericError, "invalid channel policy"
+                    raise koji.GenericError("invalid channel policy")
                 opts['channel_id'] = req_channel_id
             else:
                 logger.error("Invalid result from channel policy: %s", ruleset.last_rule())
-                raise koji.GenericError, "invalid channel policy"
+                raise koji.GenericError("invalid channel policy")
         except IndexError:
             logger.error("Invalid result from channel policy: %s", ruleset.last_rule())
-            raise koji.GenericError, "invalid channel policy"
+            raise koji.GenericError("invalid channel policy")
 
     # encode xmlrpc request
     opts['request'] = xmlrpclib.dumps(tuple(arglist), methodname=method,
@@ -589,7 +589,7 @@ def eventCondition(event, table=None):
         return """(%(table)screate_event <= %(event)d AND ( %(table)srevoke_event IS NULL OR %(event)d < %(table)srevoke_event ))""" \
             % locals()
     else:
-        raise koji.GenericError, "Invalid event: %r" % event
+        raise koji.GenericError("Invalid event: %r" % event)
 
 def readGlobalInheritance(event=None):
     c = context.cnx.cursor()
@@ -648,7 +648,7 @@ def _writeInheritanceData(tag_id, changes, clear=False):
             check_fields = ('parent_id')
         for f in fields:
             if not link.has_key(f):
-                raise koji.GenericError, "No value for %s" % f
+                raise koji.GenericError("No value for %s" % f)
     # read current data and index
     data = dict([[link['parent_id'], link] for link in readInheritanceData(tag_id)])
     for link in changes:
@@ -691,7 +691,7 @@ def _writeInheritanceData(tag_id, changes, clear=False):
             continue
         #oops, duplicate entries for a single priority
         dup_ids = [link['parent_id'] for link in dups]
-        raise koji.GenericError, "Inheritance priorities must be unique (pri %s: %r )" % (pri, dup_ids)
+        raise koji.GenericError("Inheritance priorities must be unique (pri %s: %r )" % (pri, dup_ids))
     for parent_id, link in data.iteritems():
         if not link.get('is_update'):
             continue
@@ -859,7 +859,7 @@ def _direct_pkglist_add(taginfo, pkginfo, owner, block, extra_arches, force,
     pkg = lookup_package(pkginfo, strict=False)
     if not pkg:
         if not isinstance(pkginfo, basestring):
-            raise koji.GenericError, "Invalid package: %s" % pkginfo
+            raise koji.GenericError("Invalid package: %s" % pkginfo)
     if owner is not None:
         owner = get_user(owner, strict=True)['id']
     action = 'add'
@@ -889,8 +889,8 @@ def _direct_pkglist_add(taginfo, pkginfo, owner, block, extra_arches, force,
             block = bool(block)
         if update and not force:
             #if update flag is true, require that there be a previous entry
-            raise koji.GenericError, "cannot update: tag %s has no data for package %s" \
-                    % (tag['name'], pkg['name'])
+            raise koji.GenericError("cannot update: tag %s has no data for package %s" \
+                    % (tag['name'], pkg['name']))
     else:
         #already there (possibly via inheritance)
         if owner is None:
@@ -913,12 +913,12 @@ def _direct_pkglist_add(taginfo, pkginfo, owner, block, extra_arches, force,
             #no point in adding it again with the same data
             return
         if previous['blocked'] and not block and not force:
-            raise koji.GenericError, "package %s is blocked in tag %s" % (pkg['name'], tag['name'])
+            raise koji.GenericError("package %s is blocked in tag %s" % (pkg['name'], tag['name']))
     if owner is None:
         if force:
             owner = context.session.user_id
         else:
-            raise koji.GenericError, "owner not specified"
+            raise koji.GenericError("owner not specified")
     _pkglist_add(tag_id, pkg['id'], owner, block, extra_arches)
     koji.plugin.run_callbacks('postPackageListChange', action=action, tag=tag, package=pkg, owner=owner,
                               block=block, extra_arches=extra_arches, force=force, update=update)
@@ -972,10 +972,10 @@ def pkglist_unblock(taginfo, pkginfo, force=False):
     pkglist = readPackageList(tag_id, pkgID=pkg_id, inherit=True)
     previous = pkglist.get(pkg_id, None)
     if previous is None:
-        raise koji.GenericError, "no data (blocked or otherwise) for package %s in tag %s" \
-                % (pkg['name'], tag['name'])
+        raise koji.GenericError("no data (blocked or otherwise) for package %s in tag %s" \
+                % (pkg['name'], tag['name']))
     if not previous['blocked']:
-        raise koji.GenericError, "package %s NOT blocked in tag %s" % (pkg['name'], tag['name'])
+        raise koji.GenericError("package %s NOT blocked in tag %s" % (pkg['name'], tag['name']))
     if previous['tag_id'] != tag_id:
         _pkglist_add(tag_id, pkg_id, previous['owner_id'], False, previous['extra_arches'])
     else:
@@ -1004,7 +1004,7 @@ def readPackageList(tagID=None, userID=None, pkgID=None, event=None, inherit=Fal
     Note that the returned data includes blocked entries
     """
     if tagID is None and userID is None and pkgID is None:
-        raise koji.GenericError, 'tag,user, and/or pkg must be specified'
+        raise koji.GenericError('tag,user, and/or pkg must be specified')
 
     packages = {}
     fields = (('package.id', 'package_id'), ('package.name', 'package_name'),
@@ -1104,7 +1104,7 @@ def list_tags(build=None, package=None, queryOpts=None):
       - extra_arches
     """
     if build is not None and package is not None:
-        raise koji.GenericError, 'only one of build and package may be specified'
+        raise koji.GenericError('only one of build and package may be specified')
 
     tables = ['tag_config']
     joins = ['tag ON tag.id = tag_config.tag_id',
@@ -1121,7 +1121,7 @@ def list_tags(build=None, package=None, queryOpts=None):
         # lookup build id
         buildinfo = get_build(build)
         if not buildinfo:
-            raise koji.GenericError, 'invalid build: %s' % build
+            raise koji.GenericError('invalid build: %s' % build)
         joins.append('tag_listing ON tag.id = tag_listing.tag_id')
         clauses.append('tag_listing.active = true')
         clauses.append('tag_listing.build_id = %(buildID)i')
@@ -1129,7 +1129,7 @@ def list_tags(build=None, package=None, queryOpts=None):
     elif package is not None:
         packageinfo = lookup_package(package)
         if not packageinfo:
-            raise koji.GenericError, 'invalid package: %s' % package
+            raise koji.GenericError('invalid package: %s' % package)
         fields.extend(['users.id', 'users.name', 'tag_packages.blocked', 'tag_packages.extra_arches'])
         aliases.extend(['owner_id', 'owner_name', 'blocked', 'extra_arches'])
         joins.append('tag_packages ON tag.id = tag_packages.tag_id')
@@ -1199,7 +1199,7 @@ def readTaggedBuilds(tag, event=None, inherit=False, latest=False, package=None,
     else:
         btype = lookup_name('btype', type, strict=False)
         if not btype:
-            raise koji.GenericError, 'unsupported build type: %s' % type
+            raise koji.GenericError('unsupported build type: %s' % type)
         btype_id = btype['id']
         type_join = ('JOIN build_types ON build.id = build_types.build_id '
                 'AND btype_id = %(btype_id)s')
@@ -1304,7 +1304,7 @@ def readTaggedRPMS(tag, package=None, arch=None, event=None, inherit=False, late
         elif isinstance(arch, (list, tuple)):
             clauses.append('rpminfo.arch IN %(arch)s')
         else:
-            raise koji.GenericError, 'invalid arch option: %s' % arch
+            raise koji.GenericError('invalid arch option: %s' % arch)
 
     fields, aliases = zip(*fields)
     query = QueryProcessor(tables=tables, joins=joins, clauses=clauses,
@@ -1398,7 +1398,7 @@ def readTaggedArchives(tag, package=None, event=None, inherit=False, latest=True
                        ('win_archives.platforms', 'platforms'),
                        ('win_archives.flags', 'flags')])
     else:
-        raise koji.GenericError, 'unsupported archive type: %s' % type
+        raise koji.GenericError('unsupported archive type: %s' % type)
 
     query = QueryProcessor(tables=tables, joins=joins, clauses=clauses,
                            transform=_fix_archive_row,
@@ -1447,7 +1447,7 @@ def check_tag_access(tag_id, user_id=None):
     if user_id is None:
         user_id = context.session.user_id
     if user_id is None:
-        raise koji.GenericError, "a user_id is required"
+        raise koji.GenericError("a user_id is required")
     perms = koji.auth.get_user_perms(user_id)
     override = False
     if 'admin' in perms:
@@ -1464,7 +1464,7 @@ def check_tag_access(tag_id, user_id=None):
 def assert_tag_access(tag_id, user_id=None, force=False):
     access, override, reason = check_tag_access(tag_id, user_id)
     if not access and not (override and force):
-        raise koji.ActionNotAllowed, reason
+        raise koji.ActionNotAllowed(reason)
 
 def _tag_build(tag, build, user_id=None, force=False):
     """Tag a build
@@ -1500,7 +1500,7 @@ def _direct_tag_build(tag, build, user, force=False):
     if build['state'] != koji.BUILD_STATES['COMPLETE']:
         # incomplete builds may not be tagged, not even when forced
         state = koji.BUILD_STATES[build['state']]
-        raise koji.TagError, "build %s not complete: state %s" % (nvr, state)
+        raise koji.TagError("build %s not complete: state %s" % (nvr, state))
     # see if it's already tagged
     retag = False
     table = 'tag_listing'
@@ -1512,7 +1512,7 @@ def _direct_tag_build(tag, build, user, force=False):
     if query.executeOne():
         #already tagged
         if not force:
-            raise koji.TagError, "build %s already tagged (%s)" % (nvr, tag['name'])
+            raise koji.TagError("build %s already tagged (%s)" % (nvr, tag['name']))
         #otherwise we retag
         retag = True
     if retag:
@@ -1559,7 +1559,7 @@ def _direct_untag_build(tag, build, user, strict=True, force=False):
     count = update.execute()
     if count == 0 and strict:
         nvr = "%(name)s-%(version)s-%(release)s" % build
-        raise koji.TagError, "build %s not in tag %s" % (nvr, tag['name'])
+        raise koji.TagError("build %s not in tag %s" % (nvr, tag['name']))
     koji.plugin.run_callbacks('postUntag', tag=tag, build=build, user=user, force=force, strict=strict)
 
 
@@ -1593,7 +1593,7 @@ def _grplist_add(taginfo, grpinfo, block, force, **opts):
     if previous is not None:
         #already there (possibly via inheritance)
         if previous['blocked'] and not force:
-            raise koji.GenericError, "group %s is blocked in tag %s" % (group['name'], tag['name'])
+            raise koji.GenericError("group %s is blocked in tag %s" % (group['name'], tag['name']))
         #check for duplication and grab old data for defaults
         changed = False
         for field in cfg_fields:
@@ -1678,7 +1678,7 @@ def _grplist_unblock(taginfo, grpinfo):
                            values=locals(), opts={'rowlock':True})
     blocked = query.singleValue(strict=False)
     if not blocked:
-        raise koji.GenericError, "group %s is NOT blocked in tag %s" % (group['name'], tag['name'])
+        raise koji.GenericError("group %s is NOT blocked in tag %s" % (group['name'], tag['name']))
     update = UpdateProcessor(table, values=locals(), clauses=clauses)
     update.make_revoke()
     update.execute()
@@ -1708,9 +1708,9 @@ def _grp_pkg_add(taginfo, grpinfo, pkg_name, block, force, **opts):
     groups = get_tag_groups(tag['id'], inherit=True, incl_pkgs=True, incl_reqs=False)
     grp_cfg = groups.get(group['id'], None)
     if grp_cfg is None:
-        raise koji.GenericError, "group %s not present in tag %s" % (group['name'], tag['name'])
+        raise koji.GenericError("group %s not present in tag %s" % (group['name'], tag['name']))
     elif grp_cfg['blocked']:
-        raise koji.GenericError, "group %s is blocked in tag %s" % (group['name'], tag['name'])
+        raise koji.GenericError("group %s is blocked in tag %s" % (group['name'], tag['name']))
     previous = grp_cfg['packagelist'].get(pkg_name, None)
     cfg_fields = ('type', 'basearchonly', 'requires')
     #prevent user-provided opts from doing anything strange
@@ -1718,8 +1718,8 @@ def _grp_pkg_add(taginfo, grpinfo, pkg_name, block, force, **opts):
     if previous is not None:
         #already there (possibly via inheritance)
         if previous['blocked'] and not force:
-            raise koji.GenericError, "package %s blocked in group %s, tag %s" \
-                    % (pkg_name, group['name'], tag['name'])
+            raise koji.GenericError("package %s blocked in group %s, tag %s" \
+                    % (pkg_name, group['name'], tag['name']))
         #check for duplication and grab old data for defaults
         changed = False
         for field in cfg_fields:
@@ -1800,8 +1800,8 @@ def _grp_pkg_unblock(taginfo, grpinfo, pkg_name):
                            values=locals(), opts={'rowlock':True})
     blocked = query.singleValue(strict=False)
     if not blocked:
-        raise koji.GenericError, "package %s is NOT blocked in group %s, tag %s" \
-                    % (pkg_name, grp_id, tag_id)
+        raise koji.GenericError("package %s is NOT blocked in group %s, tag %s" \
+                    % (pkg_name, grp_id, tag_id))
     update = UpdateProcessor('group_package_listing', values=locals(), clauses=clauses)
     update.make_revoke()
     update.execute()
@@ -1832,9 +1832,9 @@ def _grp_req_add(taginfo, grpinfo, reqinfo, block, force, **opts):
     groups = get_tag_groups(tag['id'], inherit=True, incl_pkgs=False, incl_reqs=True)
     grp_cfg = groups.get(group['id'], None)
     if grp_cfg is None:
-        raise koji.GenericError, "group %s not present in tag %s" % (group['name'], tag['name'])
+        raise koji.GenericError("group %s not present in tag %s" % (group['name'], tag['name']))
     elif grp_cfg['blocked']:
-        raise koji.GenericError, "group %s is blocked in tag %s" % (group['name'], tag['name'])
+        raise koji.GenericError("group %s is blocked in tag %s" % (group['name'], tag['name']))
     previous = grp_cfg['grouplist'].get(req['id'], None)
     cfg_fields = ('type', 'is_metapkg')
     #prevent user-provided opts from doing anything strange
@@ -1842,8 +1842,8 @@ def _grp_req_add(taginfo, grpinfo, reqinfo, block, force, **opts):
     if previous is not None:
         #already there (possibly via inheritance)
         if previous['blocked'] and not force:
-            raise koji.GenericError, "requirement on group %s blocked in group %s, tag %s" \
-                    % (req['name'], group['name'], tag['name'])
+            raise koji.GenericError("requirement on group %s blocked in group %s, tag %s" \
+                    % (req['name'], group['name'], tag['name']))
         #check for duplication and grab old data for defaults
         changed = False
         for field in cfg_fields:
@@ -1927,8 +1927,8 @@ def _grp_req_unblock(taginfo, grpinfo, reqinfo):
                            values=locals(), opts={'rowlock':True})
     blocked = query.singleValue(strict=False)
     if not blocked:
-        raise koji.GenericError, "group req %s is NOT blocked in group %s, tag %s" \
-                    % (req_id, grp_id, tag_id)
+        raise koji.GenericError("group req %s is NOT blocked in group %s, tag %s" \
+                    % (req_id, grp_id, tag_id))
     update = UpdateProcessor('group_req_listing', values=locals(), clauses=clauses)
     update.make_revoke()
     update.execute()
@@ -2037,7 +2037,7 @@ def readTagGroups(tag, event=None, inherit=True, incl_pkgs=True, incl_reqs=True)
 def set_host_enabled(hostname, enabled=True):
     context.session.assertPerm('admin')
     if not get_host(hostname):
-        raise koji.GenericError, 'host does not exist: %s' % hostname
+        raise koji.GenericError('host does not exist: %s' % hostname)
     c = context.cnx.cursor()
     c.execute("""UPDATE host SET enabled = %(enabled)s WHERE name = %(hostname)s""", locals())
     context.commit_pending = True
@@ -2050,15 +2050,15 @@ def add_host_to_channel(hostname, channel_name, create=False):
     context.session.assertPerm('admin')
     host = get_host(hostname)
     if host == None:
-        raise koji.GenericError, 'host does not exist: %s' % hostname
+        raise koji.GenericError('host does not exist: %s' % hostname)
     host_id = host['id']
     channel_id = get_channel_id(channel_name, create=create)
     if channel_id == None:
-        raise koji.GenericError, 'channel does not exist: %s' % channel_name
+        raise koji.GenericError('channel does not exist: %s' % channel_name)
     channels = list_channels(host_id)
     for channel in channels:
         if channel['id'] == channel_id:
-            raise koji.GenericError, 'host %s is already subscribed to the %s channel' % (hostname, channel_name)
+            raise koji.GenericError('host %s is already subscribed to the %s channel' % (hostname, channel_name))
     insert = InsertProcessor('host_channels')
     insert.set(host_id=host_id, channel_id=channel_id)
     insert.execute()
@@ -2067,11 +2067,11 @@ def remove_host_from_channel(hostname, channel_name):
     context.session.assertPerm('admin')
     host = get_host(hostname)
     if host == None:
-        raise koji.GenericError, 'host does not exist: %s' % hostname
+        raise koji.GenericError('host does not exist: %s' % hostname)
     host_id = host['id']
     channel_id = get_channel_id(channel_name)
     if channel_id == None:
-        raise koji.GenericError, 'channel does not exist: %s' % channel_name
+        raise koji.GenericError('channel does not exist: %s' % channel_name)
     found = False
     channels = list_channels(host_id)
     for channel in channels:
@@ -2079,7 +2079,7 @@ def remove_host_from_channel(hostname, channel_name):
             found = True
             break
     if not found:
-        raise koji.GenericError, 'host %s is not subscribed to the %s channel' % (hostname, channel_name)
+        raise koji.GenericError('host %s is not subscribed to the %s channel' % (hostname, channel_name))
     c = context.cnx.cursor()
     c.execute("""DELETE FROM host_channels WHERE host_id = %(host_id)d and channel_id = %(channel_id)d""", locals())
     context.commit_pending = True
@@ -2088,11 +2088,11 @@ def rename_channel(old, new):
     """Rename a channel"""
     context.session.assertPerm('admin')
     if not isinstance(new, basestring):
-        raise koji.GenericError, "new channel name must be a string"
+        raise koji.GenericError("new channel name must be a string")
     cinfo = get_channel(old, strict=True)
     dup_check = get_channel(new, strict=False)
     if dup_check:
-        raise koji.GenericError, "channel %(name)s already exists (id=%(id)i)" % dup_check
+        raise koji.GenericError("channel %(name)s already exists (id=%(id)i)" % dup_check)
     update = UpdateProcessor('channels', clauses=['id=%(id)i'], values=cinfo)
     update.set(name=new)
     update.execute()
@@ -2110,12 +2110,12 @@ def remove_channel(channel_name, force=False):
                     values=locals(), columns=['id'], opts={'limit':1})
     #XXX slow query
     if query.execute():
-        raise koji.GenericError, 'channel %s has task references' % channel_name
+        raise koji.GenericError('channel %s has task references' % channel_name)
     query = QueryProcessor(tables=['host_channels'], clauses=['channel_id=%(channel_id)i'],
                     values=locals(), columns=['host_id'], opts={'limit':1})
     if query.execute():
         if not force:
-            raise koji.GenericError, 'channel %s has host references' % channel_name
+            raise koji.GenericError('channel %s has host references' % channel_name)
         delete = """DELETE FROM host_channels WHERE channel_id=%(channel_id)i"""
         _dml(delete, locals())
     delete = """DELETE FROM channels WHERE id=%(channel_id)i"""
@@ -2451,8 +2451,8 @@ def repo_set_state(repo_id, state, check=True):
         q = """SELECT state FROM repo WHERE id = %(repo_id)s FOR UPDATE"""
         oldstate = _singleValue(q, locals())
         if oldstate > state:
-            raise koji.GenericError, "Invalid repo state transition %s->%s" \
-                    % (oldstate, state)
+            raise koji.GenericError("Invalid repo state transition %s->%s" \
+                    % (oldstate, state))
     q = """UPDATE repo SET state=%(state)s WHERE id = %(repo_id)s"""
     _dml(q, locals())
 
@@ -2590,7 +2590,7 @@ def set_tag_update(tag_id, utype, event_id=None, user_id=None):
     """Record a non-versioned tag update"""
     utype_id = koji.TAG_UPDATE_TYPES.getnum(utype)
     if utype_id is None:
-        raise koji.GenericError, "Invalid update type: %s" % utype
+        raise koji.GenericError("Invalid update type: %s" % utype)
     if event_id is None:
         event_id = get_event()
     if user_id is None:
@@ -2637,18 +2637,18 @@ def edit_build_target(buildTargetInfo, name, build_tag, dest_tag):
 
     target = lookup_build_target(buildTargetInfo)
     if not target:
-        raise koji.GenericError, 'invalid build target: %s' % buildTargetInfo
+        raise koji.GenericError('invalid build target: %s' % buildTargetInfo)
 
     buildTargetID = target['id']
 
     build_tag_object = get_tag(build_tag)
     if not build_tag_object:
-        raise koji.GenericError, "build tag '%s' does not exist" % build_tag
+        raise koji.GenericError("build tag '%s' does not exist" % build_tag)
     buildTagID = build_tag_object['id']
 
     dest_tag_object = get_tag(dest_tag)
     if not dest_tag_object:
-        raise koji.GenericError, "destination tag '%s' does not exist" % dest_tag
+        raise koji.GenericError("destination tag '%s' does not exist" % dest_tag)
     destTagID = dest_tag_object['id']
 
     if target['name'] != name:
@@ -2656,7 +2656,7 @@ def edit_build_target(buildTargetInfo, name, build_tag, dest_tag):
         id = _singleValue("""SELECT id from build_target where name = %(name)s""",
                           locals(), strict=False)
         if id is not None:
-            raise koji.GenericError, 'name "%s" is already taken by build target %i' % (name, id)
+            raise koji.GenericError('name "%s" is already taken by build target %i' % (name, id))
 
         rename = """UPDATE build_target
         SET name = %(name)s
@@ -2682,7 +2682,7 @@ def delete_build_target(buildTargetInfo):
 
     target = lookup_build_target(buildTargetInfo)
     if not target:
-        raise koji.GenericError, 'invalid build target: %s' % buildTargetInfo
+        raise koji.GenericError('invalid build target: %s' % buildTargetInfo)
 
     targetID = target['id']
 
@@ -2716,7 +2716,7 @@ def get_build_targets(info=None, event=None, buildTagID=None, destTagID=None, qu
         elif isinstance(info, int) or isinstance(info, long):
             clauses.append('build_target.id = %(info)i')
         else:
-            raise koji.GenericError, 'invalid type for lookup: %s' % type(info)
+            raise koji.GenericError('invalid type for lookup: %s' % type(info))
     if buildTagID != None:
         clauses.append('build_tag = %(buildTagID)i')
     if destTagID != None:
@@ -2734,7 +2734,7 @@ def get_build_target(info, event=None, strict=False):
     if len(targets) == 1:
         return targets[0]
     elif strict:
-        raise koji.GenericError, 'No matching build target found: %s' % info
+        raise koji.GenericError('No matching build target found: %s' % info)
     else:
         return None
 
@@ -2764,14 +2764,14 @@ def lookup_name(table, info, strict=False, create=False):
         info = koji.fixEncoding(info)
         q = """SELECT id,name FROM %s WHERE name=%%(info)s""" % table
     else:
-        raise koji.GenericError, 'invalid type for id lookup: %s' % type(info)
+        raise koji.GenericError('invalid type for id lookup: %s' % type(info))
     ret = _singleRow(q, locals(), fields, strict=False)
     if ret is None:
         if strict:
-            raise koji.GenericError, 'No such entry in table %s: %s' % (table, info)
+            raise koji.GenericError('No such entry in table %s: %s' % (table, info))
         elif create:
             if not isinstance(info, str):
-                raise koji.GenericError, 'Name must be a string'
+                raise koji.GenericError('Name must be a string')
             id = _singleValue("SELECT nextval('%s_id_seq')" % table, strict=True)
             q = """INSERT INTO %s(id,name) VALUES (%%(id)i,%%(info)s)""" % table
             _dml(q, locals())
@@ -2847,7 +2847,7 @@ def _create_tag(name, parent=None, arches=None, perm=None, locked=False, maven_s
     """Create a new tag, without access check"""
 
     if not context.opts.get('EnableMaven') and (maven_support or maven_include_all):
-        raise koji.GenericError, "Maven support not enabled"
+        raise koji.GenericError("Maven support not enabled")
 
     #see if there is already a tag by this name (active)
     if get_tag(name):
@@ -2935,7 +2935,7 @@ def get_tag(tagInfo, strict=False, event=None):
     elif isinstance(tagInfo, basestring):
         clauses.append("tag.name = %(tagInfo)s")
     else:
-        raise koji.GenericError, 'invalid type for tagInfo: %s' % type(tagInfo)
+        raise koji.GenericError('invalid type for tagInfo: %s' % type(tagInfo))
 
     data = {'tagInfo': tagInfo}
     fields, aliases = zip(*fields.items())
@@ -2944,7 +2944,7 @@ def get_tag(tagInfo, strict=False, event=None):
     result = query.executeOne()
     if not result:
         if strict:
-            raise koji.GenericError, "Invalid tagInfo: %r" % tagInfo
+            raise koji.GenericError("Invalid tagInfo: %r" % tagInfo)
         return None
     result['extra'] = get_tag_extra(result)
     return result
@@ -2987,7 +2987,7 @@ def _edit_tag(tagInfo, **kwargs):
     """Edit information for an existing tag."""
     if not context.opts.get('EnableMaven') \
                 and dslice(kwargs, ['maven_support', 'maven_include_all'], strict=False):
-        raise koji.GenericError, "Maven support not enabled"
+        raise koji.GenericError("Maven support not enabled")
 
     tag = get_tag(tagInfo, strict=True)
     if kwargs.has_key('perm'):
@@ -3012,7 +3012,7 @@ def _edit_tag(tagInfo, **kwargs):
         id = _singleValue(q, values, strict=False)
         if id is not None:
             #new name is taken
-            raise koji.GenericError, "Name %s already taken by tag %s" % (name, id)
+            raise koji.GenericError("Name %s already taken by tag %s" % (name, id))
         update = """UPDATE tag
         SET name = %(name)s
         WHERE id = %(tagID)i"""
@@ -3112,7 +3112,7 @@ def create_external_repo(name, url):
     context.session.assertPerm('admin')
 
     if get_external_repos(info=name):
-        raise koji.GenericError, 'An external repo named "%s" already exists' % name
+        raise koji.GenericError('An external repo named "%s" already exists' % name)
 
     id = get_external_repo_id(name, create=True)
     if not url.endswith('/'):
@@ -3140,7 +3140,7 @@ def get_external_repos(info=None, url=None, event=None, queryOpts=None):
         elif isinstance(info, (int, long)):
             clauses.append('id = %(info)i')
         else:
-            raise koji.GenericError, 'invalid type for lookup: %s' % type(info)
+            raise koji.GenericError('invalid type for lookup: %s' % type(info))
     if url:
         clauses.append('url = %(url)s')
 
@@ -3160,7 +3160,7 @@ def get_external_repo(info, strict=False, event=None):
         return repos[0]
     else:
         if strict:
-            raise koji.GenericError, 'invalid repo info: %s' % info
+            raise koji.GenericError('invalid repo info: %s' % info)
         else:
             return None
 
@@ -3176,7 +3176,7 @@ def edit_external_repo(info, name=None, url=None):
         existing_id = _singleValue("""SELECT id FROM external_repo WHERE name = %(name)s""",
                                    locals(), strict=False)
         if existing_id is not None:
-            raise koji.GenericError, 'name "%s" is already taken by external repo %i' % (name, existing_id)
+            raise koji.GenericError('name "%s" is already taken by external repo %i' % (name, existing_id))
 
         rename = """UPDATE external_repo SET name = %(name)s WHERE id = %(repo_id)i"""
         _dml(rename, locals())
@@ -3226,11 +3226,11 @@ def add_external_repo_to_tag(tag_info, repo_info, priority):
 
     tag_repos = get_tag_external_repos(tag_info=tag_id)
     if [tr for tr in tag_repos if tr['external_repo_id'] == repo_id]:
-        raise koji.GenericError, 'tag %s already associated with external repo %s' % \
-            (tag['name'], repo['name'])
+        raise koji.GenericError('tag %s already associated with external repo %s' % \
+            (tag['name'], repo['name']))
     if [tr for tr in tag_repos if tr['priority'] == priority]:
-        raise koji.GenericError, 'tag %s already associated with an external repo at priority %i' % \
-            (tag['name'], priority)
+        raise koji.GenericError('tag %s already associated with an external repo at priority %i' % \
+            (tag['name'], priority))
 
     insert = InsertProcessor('tag_external_repos')
     insert.set(tag_id=tag_id, external_repo_id=repo_id, priority=priority)
@@ -3248,8 +3248,8 @@ def remove_external_repo_from_tag(tag_info, repo_info):
     repo_id = repo['id']
 
     if not get_tag_external_repos(tag_info=tag_id, repo_info=repo_id):
-        raise koji.GenericError, 'external repo %s not associated with tag %s' % \
-            (repo['name'], tag['name'])
+        raise koji.GenericError('external repo %s not associated with tag %s' % \
+            (repo['name'], tag['name']))
 
     update = UpdateProcessor('tag_external_repos', values=locals(),
                 clauses=["tag_id = %(tag_id)i", "external_repo_id = %(repo_id)i"])
@@ -3269,8 +3269,8 @@ def edit_tag_external_repo(tag_info, repo_info, priority):
 
     tag_repos = get_tag_external_repos(tag_info=tag_id, repo_info=repo_id)
     if not tag_repos:
-        raise koji.GenericError, 'external repo %s not associated with tag %s' % \
-            (repo['name'], tag['name'])
+        raise koji.GenericError('external repo %s not associated with tag %s' % \
+            (repo['name'], tag['name']))
     tag_repo = tag_repos[0]
 
     if priority != tag_repo['priority']:
@@ -3367,7 +3367,7 @@ def get_user(userInfo=None, strict=False):
     elif isinstance(userInfo, str):
         clauses = ['krb_principal = %(info)s OR name = %(info)s']
     else:
-        raise koji.GenericError, 'invalid type for userInfo: %s' % type(userInfo)
+        raise koji.GenericError('invalid type for userInfo: %s' % type(userInfo))
     query = QueryProcessor(tables=['users'], columns=fields,
                            clauses=clauses, values=data)
     user = query.executeOne()
@@ -3384,11 +3384,11 @@ def find_build_id(X, strict=False):
     elif isinstance(X, dict):
         data = X
     else:
-        raise koji.GenericError, "Invalid argument: %r" % X
+        raise koji.GenericError("Invalid argument: %r" % X)
 
     if not (data.has_key('name') and data.has_key('version') and
             data.has_key('release')):
-        raise koji.GenericError, 'did not provide name, version, and release'
+        raise koji.GenericError('did not provide name, version, and release')
 
     c = context.cnx.cursor()
     q = """SELECT build.id FROM build JOIN package ON build.pkg_id=package.id
@@ -3402,7 +3402,7 @@ def find_build_id(X, strict=False):
     #log_error("%r" % r )
     if not r:
         if strict:
-            raise koji.GenericError, 'No matching build found: %r' % X
+            raise koji.GenericError('No matching build found: %r' % X)
         else:
             return None
     return r[0]
@@ -3474,7 +3474,7 @@ def get_build(buildInfo, strict=False):
 
     if not result:
         if strict:
-            raise koji.GenericError, 'No matching build found: %s' % buildInfo
+            raise koji.GenericError('No matching build found: %s' % buildInfo)
         else:
             return None
     else:
@@ -3507,7 +3507,7 @@ def get_next_release(build_info):
         r_split[0] = str(int(r_split[0]) + 1)
         release = '.'.join(r_split)
     else:
-        raise koji.BuildError, 'Unable to increment release value: %s' % release
+        raise koji.BuildError('Unable to increment release value: %s' % release)
     return release
 
 
@@ -3584,7 +3584,7 @@ def get_rpm(rpminfo, strict=False, multi=False):
     elif isinstance(rpminfo, dict):
         data = rpminfo.copy()
     else:
-        raise koji.GenericError, "Invalid argument: %r" % rpminfo
+        raise koji.GenericError("Invalid argument: %r" % rpminfo)
     clauses = []
     if data.has_key('id'):
         clauses.append("rpminfo.id=%(id)s")
@@ -3617,7 +3617,7 @@ def get_rpm(rpminfo, strict=False, multi=False):
         ret = query.executeOne()
     if not ret:
         if strict:
-            raise koji.GenericError, "No such rpm: %r" % data
+            raise koji.GenericError("No such rpm: %r" % data)
         return None
     return ret
 
@@ -3693,7 +3693,7 @@ def list_rpms(buildID=None, buildrootID=None, imageID=None, componentBuildrootID
         elif isinstance(arches, str):
             clauses.append('rpminfo.arch = %(arches)s')
         else:
-            raise koji.GenericError, 'invalid type for "arches" parameter: %s' % type(arches)
+            raise koji.GenericError('invalid type for "arches" parameter: %s' % type(arches))
 
     fields, aliases = zip(*fields)
     query = QueryProcessor(columns=fields, aliases=aliases,
@@ -3745,7 +3745,7 @@ def get_win_build(buildInfo, strict=False):
                            values={'build_id': build_id})
     result = query.executeOne()
     if strict and not result:
-        raise koji.GenericError, 'no such Windows build: %s' % buildInfo
+        raise koji.GenericError('no such Windows build: %s' % buildInfo)
     return result
 
 def get_image_build(buildInfo, strict=False):
@@ -3766,7 +3766,7 @@ def get_image_build(buildInfo, strict=False):
                            values={'build_id': build_id})
     result = query.executeOne()
     if strict and not result:
-        raise koji.GenericError, 'no such image build: %s' % buildInfo
+        raise koji.GenericError('no such image build: %s' % buildInfo)
     return result
 
 
@@ -4292,11 +4292,11 @@ def _fetchSingle(query, values, strict=False):
     numRows = len(results)
     if numRows == 0:
         if strict:
-            raise koji.GenericError, 'query returned no rows'
+            raise koji.GenericError('query returned no rows')
         else:
             return None
     elif strict and numRows > 1:
-        raise koji.GenericError, 'multiple rows returned for a single row query'
+        raise koji.GenericError('multiple rows returned for a single row query')
     else:
         return results[0]
 
@@ -4333,7 +4333,7 @@ def _singleValue(query, values=None, strict=True):
     row = _fetchSingle(query, values, strict)
     if row:
         if strict and len(row) > 1:
-            raise koji.GenericError, 'multiple fields returned for a single value query'
+            raise koji.GenericError('multiple fields returned for a single value query')
         return row[0]
     else:
         # don't need to check strict here, since that was already handled by _singleRow()
@@ -4374,7 +4374,7 @@ def get_host(hostInfo, strict=False):
     elif isinstance(hostInfo, str):
         query += """name = %(hostInfo)s"""
     else:
-        raise koji.GenericError, 'invalid type for hostInfo: %s' % type(hostInfo)
+        raise koji.GenericError('invalid type for hostInfo: %s' % type(hostInfo))
 
     return _singleRow(query, locals(), fields, strict)
 
@@ -4423,7 +4423,7 @@ def get_channel(channelInfo, strict=False):
     elif isinstance(channelInfo, str):
         query += """name = %(channelInfo)s"""
     else:
-        raise koji.GenericError, 'invalid type for channelInfo: %s' % type(channelInfo)
+        raise koji.GenericError('invalid type for channelInfo: %s' % type(channelInfo))
 
     return _singleRow(query, locals(), fields, strict)
 
@@ -4510,12 +4510,12 @@ def get_buildroot(buildrootID, strict=False):
     result = query_buildroots(buildrootID=buildrootID)
     if len(result) == 0:
         if strict:
-            raise koji.GenericError, "No such buildroot: %r" % buildrootID
+            raise koji.GenericError("No such buildroot: %r" % buildrootID)
         else:
             return None
     if len(result) > 1:
         #this should be impossible
-        raise koji.GenericError, "More that one buildroot with id: %i" % buildrootID
+        raise koji.GenericError("More that one buildroot with id: %i" % buildrootID)
     return result[0]
 
 def list_channels(hostID=None):
@@ -4539,7 +4539,7 @@ def new_package(name, strict=True):
     if row:
         (pkg_id,) = row
         if strict:
-            raise koji.GenericError, "Package already exists [id %d]" % pkg_id
+            raise koji.GenericError("Package already exists [id %d]" % pkg_id)
     else:
         q = """SELECT nextval('package_id_seq')"""
         c.execute(q)
@@ -4555,11 +4555,11 @@ def add_volume(name, strict=True):
     context.session.assertPerm('admin')
     voldir = koji.pathinfo.volumedir(name)
     if not os.path.isdir(voldir):
-        raise koji.GenericError, 'please create the volume directory first'
+        raise koji.GenericError('please create the volume directory first')
     if strict:
         volinfo = lookup_name('volume', name, strict=False)
         if volinfo:
-            raise koji.GenericError, 'volume %s already exists' % name
+            raise koji.GenericError('volume %s already exists' % name)
     volinfo = lookup_name('volume', name, strict=False, create=True)
     return volinfo
 
@@ -4570,7 +4570,7 @@ def remove_volume(volume):
     query = QueryProcessor(tables=['build'], clauses=['volume_id=%(id)i'],
                     values=volinfo, columns=['id'], opts={'limit':1})
     if query.execute():
-        raise koji.GenericError, 'volume %(name)s has build references' % volinfo
+        raise koji.GenericError('volume %(name)s has build references' % volinfo)
     delete = """DELETE FROM volume WHERE id=%(id)i"""
     _dml(delete, volinfo)
 
@@ -4585,16 +4585,16 @@ def change_build_volume(build, volume, strict=True):
     binfo = get_build(build, strict=True)
     if binfo['volume_id'] == volinfo['id']:
         if strict:
-            raise koji.GenericError, "Build %(nvr)s already on volume %(volume_name)s" % binfo
+            raise koji.GenericError("Build %(nvr)s already on volume %(volume_name)s" % binfo)
         else:
             #nothing to do
             return
     state = koji.BUILD_STATES[binfo['state']]
     if state not in ['COMPLETE', 'DELETED']:
-        raise koji.GenericError, "Build %s is %s" % (binfo['nvr'], state)
+        raise koji.GenericError("Build %s is %s" % (binfo['nvr'], state))
     voldir = koji.pathinfo.volumedir(volinfo['name'])
     if not os.path.isdir(voldir):
-        raise koji.GenericError, "Directory entry missing for volume %(name)s" % volinfo
+        raise koji.GenericError("Directory entry missing for volume %(name)s" % volinfo)
 
     #more sanity checks
     for check_vol in list_volumes():
@@ -4611,9 +4611,9 @@ def change_build_volume(build, volume, strict=True):
         elif check_vol['id'] == volinfo['id']:
             # the volume we are moving to
             if os.path.lexists(checkdir):
-                raise koji.GenericError, "Destination directory exists: %s" % checkdir
+                raise koji.GenericError("Destination directory exists: %s" % checkdir)
         elif os.path.lexists(checkdir):
-            raise koji.GenericError, "Unexpected cross-volume content: %s" % checkdir
+            raise koji.GenericError("Unexpected cross-volume content: %s" % checkdir)
 
     # First copy the build dir(s)
     dir_moves = []
@@ -4671,11 +4671,11 @@ def new_build(data):
         #see if there's a package name
         name = data.get('name')
         if not name:
-            raise koji.GenericError, "No name or package id provided for build"
+            raise koji.GenericError("No name or package id provided for build")
         data['pkg_id'] = new_package(name, strict=False)
     for f in ('version', 'release', 'epoch'):
         if not data.has_key(f):
-            raise koji.GenericError, "No %s value for build" % f
+            raise koji.GenericError("No %s value for build" % f)
     if 'extra' in data:
         try:
             data['extra'] = json.dumps(data['extra'])
@@ -4823,7 +4823,7 @@ def import_build(srpm, rpms, brmap=None, task_id=None, build_id=None, logs=None)
     for relpath in [srpm] + rpms:
         fn = "%s/%s" % (uploadpath, relpath)
         if not os.path.exists(fn):
-            raise koji.GenericError, "no such file: %s" % fn
+            raise koji.GenericError("no such file: %s" % fn)
 
     rpms = check_noarch_rpms(uploadpath, rpms)
 
@@ -4841,7 +4841,7 @@ def import_build(srpm, rpms, brmap=None, task_id=None, build_id=None, logs=None)
     build = koji.get_header_fields(fn, ('name', 'version', 'release', 'epoch',
                                         'sourcepackage'))
     if build['sourcepackage'] != 1:
-        raise koji.GenericError, "not a source package: %s" % fn
+        raise koji.GenericError("not a source package: %s" % fn)
     build['task_id'] = task_id
     if build_id is None:
         build_id = new_build(build)
@@ -4854,10 +4854,10 @@ def import_build(srpm, rpms, brmap=None, task_id=None, build_id=None, logs=None)
         koji.plugin.run_callbacks('preBuildStateChange', attribute='state', old=binfo['state'], new=st_complete, info=binfo)
         for key in ('name', 'version', 'release', 'epoch', 'task_id'):
             if build[key] != binfo[key]:
-                raise koji.GenericError, "Unable to complete build: %s mismatch (build: %s, rpm: %s)" % (key, binfo[key], build[key])
+                raise koji.GenericError("Unable to complete build: %s mismatch (build: %s, rpm: %s)" % (key, binfo[key], build[key]))
         if binfo['state'] != koji.BUILD_STATES['BUILDING']:
-            raise koji.GenericError, "Unable to complete build: state is %s" \
-                    % koji.BUILD_STATES[binfo['state']]
+            raise koji.GenericError("Unable to complete build: state is %s" \
+                    % koji.BUILD_STATES[binfo['state']])
         #update build state
         update = """UPDATE build SET state=%(st_complete)i,completion_time=NOW()
         WHERE id=%(build_id)i"""
@@ -4887,7 +4887,7 @@ def import_rpm(fn, buildinfo=None, brootid=None, wrapper=False, fileinfo=None):
     Designed to be called from import_build.
     """
     if not os.path.exists(fn):
-        raise koji.GenericError, "no such file: %s" % fn
+        raise koji.GenericError("no such file: %s" % fn)
 
     #read rpm info
     hdr = koji.get_rpm_header(fn)
@@ -4900,7 +4900,7 @@ def import_rpm(fn, buildinfo=None, brootid=None, wrapper=False, fileinfo=None):
     basename = os.path.basename(fn)
     expected = "%(name)s-%(version)s-%(release)s.%(arch)s.rpm" % rpminfo
     if basename != expected:
-        raise koji.GenericError, "bad filename: %s (expected %s)" % (basename, expected)
+        raise koji.GenericError("bad filename: %s (expected %s)" % (basename, expected))
 
     if buildinfo is None:
         #figure it out for ourselves
@@ -4917,11 +4917,11 @@ def import_rpm(fn, buildinfo=None, brootid=None, wrapper=False, fileinfo=None):
             if buildinfo is None:
                 #XXX - handle case where package is not a source rpm
                 #      and we still need to create a new build
-                raise koji.GenericError, 'No matching build'
+                raise koji.GenericError('No matching build')
             state = koji.BUILD_STATES[buildinfo['state']]
             if state in ('FAILED', 'CANCELED', 'DELETED'):
                 nvr = "%(name)s-%(version)s-%(release)s" % buildinfo
-                raise koji.GenericError, "Build is %s: %s" % (state, nvr)
+                raise koji.GenericError("Build is %s: %s" % (state, nvr))
     elif not wrapper:
         # only enforce the srpm name matching the build for non-wrapper rpms
         srpmname = "%(name)s-%(version)s-%(release)s.src.rpm" % buildinfo
@@ -4929,11 +4929,11 @@ def import_rpm(fn, buildinfo=None, brootid=None, wrapper=False, fileinfo=None):
         #itself (for the srpm)
         if rpminfo['sourcepackage'] != 1:
             if rpminfo['sourcerpm'] != srpmname:
-                raise koji.GenericError, "srpm mismatch for %s: %s (expected %s)" \
-                        % (fn, rpminfo['sourcerpm'], srpmname)
+                raise koji.GenericError("srpm mismatch for %s: %s (expected %s)" \
+                        % (fn, rpminfo['sourcerpm'], srpmname))
         elif basename != srpmname:
-            raise koji.GenericError, "srpm mismatch for %s: %s (expected %s)" \
-                    % (fn, basename, srpmname)
+            raise koji.GenericError("srpm mismatch for %s: %s (expected %s)" \
+                    % (fn, basename, srpmname))
 
     # if we're adding an rpm to it, then this build is of rpm type
     # harmless if build already has this type
@@ -5448,10 +5448,10 @@ def add_external_rpm(rpminfo, external_repo, strict=True):
         ('buildtime', (int, long)))
     for field, allowed in dtypes:
         if not rpminfo.has_key(field):
-            raise koji.GenericError, "%s field missing: %r" % (field, rpminfo)
+            raise koji.GenericError("%s field missing: %r" % (field, rpminfo))
         if not isinstance(rpminfo[field], allowed):
             #this will catch unwanted NULLs
-            raise koji.GenericError, "Invalid value for %s: %r" % (field, rpminfo[field])
+            raise koji.GenericError("Invalid value for %s: %r" % (field, rpminfo[field]))
     #TODO: more sanity checks for payloadhash
 
     #Check to see if we have it
@@ -5461,10 +5461,10 @@ def add_external_rpm(rpminfo, external_repo, strict=True):
     if previous:
         disp = "%(name)s-%(version)s-%(release)s.%(arch)s@%(external_repo_name)s" % previous
         if strict:
-            raise koji.GenericError, "external rpm already exists: %s" % disp
+            raise koji.GenericError("external rpm already exists: %s" % disp)
         elif data['payloadhash'] != previous['payloadhash']:
-            raise koji.GenericError, "hash changed for external rpm: %s (%s -> %s)" \
-                    % (disp, previous['payloadhash'], data['payloadhash'])
+            raise koji.GenericError("hash changed for external rpm: %s (%s -> %s)" \
+                    % (disp, previous['payloadhash'], data['payloadhash']))
         else:
             return previous
 
@@ -5523,7 +5523,7 @@ def import_build_in_place(build):
             # TODO - check contents against db
             return prev['id']
         elif state not in ('FAILED', 'CANCELED'):
-            raise koji.GenericError, "build already exists (%s): %r" % (state, build)
+            raise koji.GenericError("build already exists (%s): %r" % (state, build))
         #otherwise try to reimport
     bdir = koji.pathinfo.build(build)
     srpm = None
@@ -5534,7 +5534,7 @@ def import_build_in_place(build):
     if os.path.isdir(srcdir):
         for basename in os.listdir(srcdir):
             if basename != srpmname:
-                raise koji.GenericError, "unexpected file: %s" % basename
+                raise koji.GenericError("unexpected file: %s" % basename)
             srpm = "%s/%s" % (srcdir, basename)
     for arch in os.listdir(bdir):
         if arch == 'src':
@@ -5544,19 +5544,19 @@ def import_build_in_place(build):
             continue
         adir = "%s/%s" % (bdir, arch)
         if not os.path.isdir(adir):
-            raise koji.GenericError, "out of place file: %s" % adir
+            raise koji.GenericError("out of place file: %s" % adir)
         for basename in os.listdir(adir):
             fn = "%s/%s" % (adir, basename)
             if not os.path.isfile(fn):
-                raise koji.GenericError, "unexpected non-regular file: %s" % fn
+                raise koji.GenericError("unexpected non-regular file: %s" % fn)
             if fn[-4:] != '.rpm':
-                raise koji.GenericError, "out of place file: %s" % adir
+                raise koji.GenericError("out of place file: %s" % adir)
             #check sourcerpm field
             hdr = koji.get_rpm_header(fn)
             sourcerpm = hdr[rpm.RPMTAG_SOURCERPM]
             if sourcerpm != srpmname:
-                raise koji.GenericError, "srpm mismatch for %s: %s (expected %s)" \
-                        % (fn, sourcerpm, srpmname)
+                raise koji.GenericError("srpm mismatch for %s: %s (expected %s)" \
+                        % (fn, sourcerpm, srpmname))
             rpms.append(fn)
     koji.plugin.run_callbacks('preImport', type='build', in_place=True, srpm=srpm, rpms=rpms)
     # actually import
@@ -5603,13 +5603,13 @@ def merge_scratch(task_id):
     try:
         task_info = task.getInfo(request=True)
     except koji.GenericError:
-        raise koji.ImportError, 'invalid task: %s' % task_id
+        raise koji.ImportError('invalid task: %s' % task_id)
     if task_info['state'] != koji.TASK_STATES['CLOSED']:
-        raise koji.ImportError, 'task %s did not complete successfully' % task_id
+        raise koji.ImportError('task %s did not complete successfully' % task_id)
     if task_info['method'] != 'build':
-        raise koji.ImportError, 'task %s is not a build task' % task_id
+        raise koji.ImportError('task %s is not a build task' % task_id)
     if len(task_info['request']) < 3 or not task_info['request'][2].get('scratch'):
-        raise koji.ImportError, 'task %s is not a scratch build' % task_id
+        raise koji.ImportError('task %s is not a scratch build' % task_id)
 
     # sanity check the task, and extract data required for import
     srpm = None
@@ -5626,8 +5626,8 @@ def merge_scratch(task_id):
                     srpm = srpm_name
                 else:
                     if srpm != srpm_name:
-                        raise koji.ImportError, 'task srpm names do not match: %s, %s' % \
-                              (srpm, srpm_name)
+                        raise koji.ImportError('task srpm names do not match: %s, %s' % \
+                              (srpm, srpm_name))
             elif output.endswith('.noarch.rpm'):
                 continue
             elif output.endswith('.rpm'):
@@ -5635,34 +5635,34 @@ def merge_scratch(task_id):
                 if 'arch' not in info:
                     info['arch'] = rpminfo['arch']
                 elif info['arch'] != rpminfo['arch']:
-                    raise koji.ImportError, 'multiple arches generated by task %s: %s, %s' % \
-                          (child['id'], info['arch'], rpminfo['arch'])
+                    raise koji.ImportError('multiple arches generated by task %s: %s, %s' % \
+                          (child['id'], info['arch'], rpminfo['arch']))
                 info['rpms'].append(output)
             elif output.endswith('.log'):
                 info['logs'].append(output)
         if not info['rpms']:
             continue
         if not info['logs']:
-            raise koji.ImportError, 'task %s is missing logs' % child['id']
+            raise koji.ImportError('task %s is missing logs' % child['id'])
         buildroots = query_buildroots(taskID=child['id'],
                                       queryOpts={'order': '-id', 'limit': 1})
         if not buildroots:
-            raise koji.ImportError, 'no buildroot associated with task %s' % child['id']
+            raise koji.ImportError('no buildroot associated with task %s' % child['id'])
         info['buildroot_id'] = buildroots[0]['id']
         tasks[child['id']] = info
     if not tasks:
-        raise koji.ImportError, 'nothing to do for task %s' % task_id
+        raise koji.ImportError('nothing to do for task %s' % task_id)
 
     # sanity check the build
     build_nvr = koji.parse_NVRA(srpm)
     build = get_build(build_nvr)
     if not build:
-        raise koji.ImportError, 'no such build: %(name)s-%(version)s-%(release)s' % \
-              build_nvr
+        raise koji.ImportError('no such build: %(name)s-%(version)s-%(release)s' % \
+              build_nvr)
     if build['state'] != koji.BUILD_STATES['COMPLETE']:
-        raise koji.ImportError, '%s did not complete successfully' % build['nvr']
+        raise koji.ImportError('%s did not complete successfully' % build['nvr'])
     if not build['task_id']:
-        raise koji.ImportError, 'no task for %s' % build['nvr']
+        raise koji.ImportError('no task for %s' % build['nvr'])
     build_task_info = Task(build['task_id']).getInfo(request=True)
     # Intentionally skip checking the build task state.
     # There are cases where the build can be valid even though the task has failed,
@@ -5670,26 +5670,26 @@ def merge_scratch(task_id):
 
     # compare the task and build and make sure they are compatible with importing
     if task_info['request'][0] != build_task_info['request'][0]:
-        raise koji.ImportError, 'SCM URLs for the task and build do not match: %s, %s' % \
-              (task_info['request'][0], build_task_info['request'][0])
+        raise koji.ImportError('SCM URLs for the task and build do not match: %s, %s' % \
+              (task_info['request'][0], build_task_info['request'][0]))
     build_arches = set()
     for rpm in list_rpms(buildID=build['id']):
         if rpm['arch'] == 'src':
             build_srpm = '%s.src.rpm' % rpm['nvr']
             if srpm != build_srpm:
-                raise koji.ImportError, 'task and build srpm names do not match: %s, %s' % \
-                      (srpm, build_srpm)
+                raise koji.ImportError('task and build srpm names do not match: %s, %s' % \
+                      (srpm, build_srpm))
         elif rpm['arch'] == 'noarch':
             continue
         else:
             build_arches.add(rpm['arch'])
     if not build_arches:
-        raise koji.ImportError, 'no arch-specific rpms found for %s' % build['nvr']
+        raise koji.ImportError('no arch-specific rpms found for %s' % build['nvr'])
     task_arches = set([t['arch'] for t in tasks.values()])
     overlapping_arches = task_arches.intersection(build_arches)
     if overlapping_arches:
-        raise koji.ImportError, 'task %s and %s produce rpms with the same arches: %s' % \
-              (task_info['id'], build['nvr'], ', '.join(overlapping_arches))
+        raise koji.ImportError('task %s and %s produce rpms with the same arches: %s' % \
+              (task_info['id'], build['nvr'], ', '.join(overlapping_arches)))
 
     # everything looks good, do the import
     for task_id, info in tasks.items():
@@ -5737,7 +5737,7 @@ def get_archive_type(filename=None, type_name=None, type_id=None, strict=False):
         # we handle that below
         pass
     else:
-        raise koji.GenericError, 'one of filename, type_name, or type_id must be specified'
+        raise koji.GenericError('one of filename, type_name, or type_id must be specified')
 
     parts = filename.split('.')
 
@@ -5752,10 +5752,10 @@ def get_archive_type(filename=None, type_name=None, type_id=None, strict=False):
             return results[0]
         elif len(results) > 1:
             # this should never happen, and is a misconfiguration in the database
-            raise koji.GenericError, 'multiple matches for file extension: %s' % ext
+            raise koji.GenericError('multiple matches for file extension: %s' % ext)
     #otherwise
     if strict:
-        raise koji.GenericError, 'unsupported file extension: %s' % ext
+        raise koji.GenericError('unsupported file extension: %s' % ext)
     else:
         return None
 
@@ -5772,8 +5772,8 @@ def new_maven_build(build, maven_info):
         # already exists, verify that it matches
         for field in ('group_id', 'artifact_id', 'version'):
             if current_maven_info[field] != maven_info[field]:
-                raise koji.BuildError, '%s mismatch (current: %s, new: %s)' % \
-                    (field, current_maven_info[field], maven_info[field])
+                raise koji.BuildError('%s mismatch (current: %s, new: %s)' % \
+                    (field, current_maven_info[field], maven_info[field]))
     else:
         maven_info['build_id'] = build['id']
         data = dslice(maven_info, ['build_id', 'group_id', 'artifact_id', 'version'])
@@ -5853,7 +5853,7 @@ def old_image_data(old_image_id):
     ret = query.executeOne()
 
     if not ret:
-        raise koji.GenericError, 'no old image with ID: %i' % old_image_id
+        raise koji.GenericError('no old image with ID: %i' % old_image_id)
     return ret
 
 def check_old_image_files(old):
@@ -5868,15 +5868,15 @@ def check_old_image_files(old):
     img_dir = os.path.join(*parts)
     img_path = os.path.join(img_dir, old['filename'])
     if not os.path.exists(img_path):
-        raise koji.GenericError, "Image file is missing: %s" % img_path
+        raise koji.GenericError("Image file is missing: %s" % img_path)
     if os.path.islink(img_path):
-        raise koji.GenericError, "Image file is a symlink: %s" % img_path
+        raise koji.GenericError("Image file is a symlink: %s" % img_path)
     if not os.path.isfile(img_path):
-        raise koji.GenericError, "Not a regular file: %s" % img_path
+        raise koji.GenericError("Not a regular file: %s" % img_path)
     img_size = os.path.getsize(img_path)
     if img_size != old['filesize']:
-        raise koji.GenericError, "Size mismatch for %s (%i != %i)" % \
-                    (img_path, img_size, old['filesize'])
+        raise koji.GenericError("Size mismatch for %s (%i != %i)" % \
+                    (img_path, img_size, old['filesize']))
     # old images always used sha256 hashes
     sha256sum = hashlib.sha256()
     image_fo = file(img_path, 'r')
@@ -5887,8 +5887,8 @@ def check_old_image_files(old):
             break
     img_hash = sha256sum.hexdigest()
     if img_hash != old['hash']:
-        raise koji.GenericError, "Hash mismatch for %s (%i != %i)" % \
-                    (img_path, img_hash, old['hash'])
+        raise koji.GenericError("Hash mismatch for %s (%i != %i)" % \
+                    (img_path, img_hash, old['hash']))
     # file looks ok
     old['path'] = img_path
     old['dir'] = img_dir
@@ -5907,7 +5907,7 @@ def check_old_image_files(old):
         else:
             extra.append(out_file)
     if extra:
-        raise koji.GenericError, "Unexpected files under %s: %r" % (img_dir, extra)
+        raise koji.GenericError("Unexpected files under %s: %r" % (img_dir, extra))
 
 
 def import_old_image(old, name, version):
@@ -5946,7 +5946,7 @@ def import_old_image(old, name, version):
         archivetype = get_archive_type(filename=fn)
         logger.debug('image type we are migrating is: %s' % archivetype)
         if not archivetype:
-            raise koji.BuildError, 'Unsupported image type'
+            raise koji.BuildError('Unsupported image type')
         imgdata = dict(arch=old['arch'])
         archives.append(import_archive(fullpath, binfo, 'image', imgdata, buildroot_id=br_id))
 
@@ -6012,7 +6012,7 @@ def import_archive_internal(filepath, buildinfo, type, typeInfo, buildroot_id=No
     if metadata_only:
         filepath = None
     elif not os.path.exists(filepath):
-        raise koji.GenericError, 'no such file: %s' % filepath
+        raise koji.GenericError('no such file: %s' % filepath)
 
     archiveinfo = {'buildroot_id': buildroot_id}
     archiveinfo['build_id'] = buildinfo['id']
@@ -6057,7 +6057,7 @@ def import_archive_internal(filepath, buildinfo, type, typeInfo, buildroot_id=No
     archiveinfo['type_id'] = archivetype['id']
     btype = lookup_name('btype', type, strict=False)
     if btype is None:
-        raise koji.BuildError, 'unsupported archive type: %s' % type
+        raise koji.BuildError('unsupported archive type: %s' % type)
     archiveinfo['btype_id'] = btype['id']
 
     # cg extra data
@@ -6082,12 +6082,12 @@ def import_archive_internal(filepath, buildinfo, type, typeInfo, buildroot_id=No
             pom_maveninfo = koji.pom_to_maven_info(pom_info)
             # sanity check: Maven info from pom must match the user-supplied typeInfo
             if koji.mavenLabel(pom_maveninfo) != koji.mavenLabel(typeInfo):
-                raise koji.BuildError, 'Maven info from .pom file (%s) does not match user-supplied typeInfo (%s)' % \
-                    (koji.mavenLabel(pom_maveninfo), koji.mavenLabel(typeInfo))
+                raise koji.BuildError('Maven info from .pom file (%s) does not match user-supplied typeInfo (%s)' % \
+                    (koji.mavenLabel(pom_maveninfo), koji.mavenLabel(typeInfo)))
             # sanity check: the filename of the pom file must match <artifactId>-<version>.pom
             if filename != '%(artifact_id)s-%(version)s.pom' % typeInfo:
-                raise koji.BuildError, 'Maven info (%s) is not consistent with pom filename (%s)' % \
-                      (koji.mavenLabel(typeInfo), filename)
+                raise koji.BuildError('Maven info (%s) is not consistent with pom filename (%s)' % \
+                      (koji.mavenLabel(typeInfo), filename))
 
         insert = InsertProcessor('maven_archives', data=dslice(typeInfo, ('group_id', 'artifact_id', 'version')))
         insert.set(archive_id=archive_id)
@@ -6107,7 +6107,7 @@ def import_archive_internal(filepath, buildinfo, type, typeInfo, buildroot_id=No
         relpath = typeInfo['relpath'].strip('/')
         insert.set(relpath=relpath)
         if not typeInfo['platforms']:
-            raise koji.BuildError, 'no value for platforms'
+            raise koji.BuildError('no value for platforms')
         insert.set(platforms=' '.join(typeInfo['platforms']))
         if typeInfo['flags']:
             insert.set(flags=' '.join(typeInfo['flags']))
@@ -6187,12 +6187,12 @@ def add_rpm_sig(an_rpm, sighdr):
     #calling function should perform permission checks, if applicable
     rinfo = get_rpm(an_rpm, strict=True)
     if rinfo['external_repo_id']:
-        raise koji.GenericError, "Not an internal rpm: %s (from %s)" \
-                % (an_rpm, rinfo['external_repo_name'])
+        raise koji.GenericError("Not an internal rpm: %s (from %s)" \
+                % (an_rpm, rinfo['external_repo_name']))
     binfo = get_build(rinfo['build_id'])
     builddir = koji.pathinfo.build(binfo)
     if not os.path.isdir(builddir):
-        raise koji.GenericError, "No such directory: %s" % builddir
+        raise koji.GenericError("No such directory: %s" % builddir)
     rawhdr = koji.RawHeader(sighdr)
     sigmd5 = koji.hex_string(rawhdr.get(koji.RPM_SIGTAG_MD5))
     if sigmd5 == rinfo['payloadhash']:
@@ -6212,7 +6212,7 @@ def add_rpm_sig(an_rpm, sighdr):
         sigmd5 = koji.hex_string(sigmd5)
         if sigmd5 != rinfo['payloadhash']:
             nvra = "%(name)s-%(version)s-%(release)s.%(arch)s" % rinfo
-            raise koji.GenericError, "wrong md5 for %s: %s" % (nvra, sigmd5)
+            raise koji.GenericError("wrong md5 for %s: %s" % (nvra, sigmd5))
     if not sigkey:
         sigkey = ''
         #we use the sigkey='' to represent unsigned in the db (so that uniqueness works)
@@ -6226,7 +6226,7 @@ def add_rpm_sig(an_rpm, sighdr):
     if rows:
         #TODO[?] - if sighash is the same, handle more gracefully
         nvra = "%(name)s-%(version)s-%(release)s.%(arch)s" % rinfo
-        raise koji.GenericError, "Signature already exists for package %s, key %s" % (nvra, sigkey)
+        raise koji.GenericError("Signature already exists for package %s, key %s" % (nvra, sigkey))
     koji.plugin.run_callbacks('preRPMSign', sigkey=sigkey, sighash=sighash, build=binfo, rpm=rinfo)
     insert = """INSERT INTO rpmsigs(rpm_id, sigkey, sighash)
     VALUES (%(rpm_id)s, %(sigkey)s, %(sighash)s)"""
@@ -6243,9 +6243,9 @@ def _scan_sighdr(sighdr, fn):
     """Splices sighdr with other headers from fn and queries (no payload)"""
     # This is hackish, but it works
     if not os.path.exists(fn):
-        raise koji.GenericError, "No such path: %s" % fn
+        raise koji.GenericError("No such path: %s" % fn)
     if not os.path.isfile(fn):
-        raise koji.GenericError, "Not a regular file: %s" % fn
+        raise koji.GenericError("Not a regular file: %s" % fn)
     #XXX should probably add an option to splice_rpm_sighdr to handle this instead
     sig_start, sigsize = koji.find_rpm_sighdr(fn)
     hdr_start = sig_start + sigsize
@@ -6278,9 +6278,9 @@ def check_rpm_sig(an_rpm, sigkey, sighdr):
     builddir = koji.pathinfo.build(binfo)
     rpm_path = "%s/%s" % (builddir, koji.pathinfo.rpm(rinfo))
     if not os.path.exists(rpm_path):
-        raise koji.GenericError, "No such path: %s" % rpm_path
+        raise koji.GenericError("No such path: %s" % rpm_path)
     if not os.path.isfile(rpm_path):
-        raise koji.GenericError, "Not a regular file: %s" % rpm_path
+        raise koji.GenericError("Not a regular file: %s" % rpm_path)
     fd, temp = tempfile.mkstemp()
     os.close(fd)
     try:
@@ -6304,8 +6304,8 @@ def check_rpm_sig(an_rpm, sigkey, sighdr):
     else:
         found_key = koji.get_sigpacket_key_id(raw_key)
     if sigkey != found_key:
-        raise koji.GenericError, "Signature key mismatch: got %s, expected %s" \
-                              % (found_key, sigkey)
+        raise koji.GenericError("Signature key mismatch: got %s, expected %s" \
+                              % (found_key, sigkey))
     os.unlink(temp)
 
 
@@ -6325,22 +6325,22 @@ def write_signed_rpm(an_rpm, sigkey, force=False):
     """Write a signed copy of the rpm"""
     rinfo = get_rpm(an_rpm, strict=True)
     if rinfo['external_repo_id']:
-        raise koji.GenericError, "Not an internal rpm: %s (from %s)" \
-                % (an_rpm, rinfo['external_repo_name'])
+        raise koji.GenericError("Not an internal rpm: %s (from %s)" \
+                % (an_rpm, rinfo['external_repo_name']))
     binfo = get_build(rinfo['build_id'])
     nvra = "%(name)s-%(version)s-%(release)s.%(arch)s" % rinfo
     builddir = koji.pathinfo.build(binfo)
     rpm_path = "%s/%s" % (builddir, koji.pathinfo.rpm(rinfo))
     if not os.path.exists(rpm_path):
-        raise koji.GenericError, "No such path: %s" % rpm_path
+        raise koji.GenericError("No such path: %s" % rpm_path)
     if not os.path.isfile(rpm_path):
-        raise koji.GenericError, "Not a regular file: %s" % rpm_path
+        raise koji.GenericError("Not a regular file: %s" % rpm_path)
     #make sure we have it in the db
     rpm_id = rinfo['id']
     q = """SELECT sighash FROM rpmsigs WHERE rpm_id=%(rpm_id)i AND sigkey=%(sigkey)s"""
     row = _fetchSingle(q, locals())
     if not row:
-        raise koji.GenericError, "No cached signature for package %s, key %s" % (nvra, sigkey)
+        raise koji.GenericError("No cached signature for package %s, key %s" % (nvra, sigkey))
     (sighash,) = row
     signedpath = "%s/%s" % (builddir, koji.pathinfo.signed(rinfo, sigkey))
     if os.path.exists(signedpath):
@@ -6451,7 +6451,7 @@ def query_history(tables=None, **kwargs):
     else:
         for table in tables:
             if table not in table_fields:
-                raise koji.GenericError, "Unknown history table: %s" % table
+                raise koji.GenericError("Unknown history table: %s" % table)
     ret = {}
     for table in tables:
         fields = {}
@@ -6863,25 +6863,25 @@ def delete_build(build, strict=True, min_ref_age=604800):
     refs = build_references(binfo['id'], limit=10)
     if refs['tags']:
         if strict:
-            raise koji.GenericError, "Cannot delete build, tagged: %s" % refs['tags']
+            raise koji.GenericError("Cannot delete build, tagged: %s" % refs['tags'])
         return False
     if refs['rpms']:
         if strict:
-            raise koji.GenericError, "Cannot delete build, used in buildroots: %s" % refs['rpms']
+            raise koji.GenericError("Cannot delete build, used in buildroots: %s" % refs['rpms'])
         return False
     if refs['archives']:
         if strict:
-            raise koji.GenericError, "Cannot delete build, used in archive buildroots: %s" % refs['archives']
+            raise koji.GenericError("Cannot delete build, used in archive buildroots: %s" % refs['archives'])
         return False
     if refs['images']:
         if strict:
-            raise koji.GenericError, "Cannot delete build, used in images: %r" % refs['images']
+            raise koji.GenericError("Cannot delete build, used in images: %r" % refs['images'])
         return False
     if refs['last_used']:
         age = time.time() - refs['last_used']
         if age < min_ref_age:
             if strict:
-                raise koji.GenericError, "Cannot delete build, used in recent buildroot"
+                raise koji.GenericError("Cannot delete build, used in recent buildroot")
             return False
     #otherwise we can delete it
     _delete_build(binfo)
@@ -6927,11 +6927,11 @@ def _delete_build(binfo):
     for filedir in dirs_to_clear:
         rv = os.system(r"find '%s' -xdev \! -type d -print0 |xargs -0 rm -f" % filedir)
         if rv != 0:
-            raise koji.GenericError, 'file removal failed (code %r) for %s' % (rv, filedir)
+            raise koji.GenericError('file removal failed (code %r) for %s' % (rv, filedir))
         #and clear out the emptied dirs
         rv = os.system(r"find '%s' -xdev -depth -type d -print0 |xargs -0 rmdir" % filedir)
         if rv != 0:
-            raise koji.GenericError, 'directory removal failed (code %r) for %s' % (rv, filedir)
+            raise koji.GenericError('directory removal failed (code %r) for %s' % (rv, filedir))
     koji.plugin.run_callbacks('postBuildStateChange', attribute='state', old=binfo['state'], new=st_deleted, info=binfo)
 
 def reset_build(build):
@@ -7003,11 +7003,11 @@ def reset_build(build):
     for filedir in dirs_to_clear:
         rv = os.system(r"find '%s' -xdev \! -type d -print0 |xargs -0 rm -f" % filedir)
         if rv != 0:
-            raise koji.GenericError, 'file removal failed (code %r) for %s' % (rv, filedir)
+            raise koji.GenericError('file removal failed (code %r) for %s' % (rv, filedir))
         #and clear out the emptied dirs
         rv = os.system(r"find '%s' -xdev -depth -type d -print0 |xargs -0 rmdir" % filedir)
         if rv != 0:
-            raise koji.GenericError, 'directory removal failed (code %r) for %s' % (rv, filedir)
+            raise koji.GenericError('directory removal failed (code %r) for %s' % (rv, filedir))
     koji.plugin.run_callbacks('postBuildStateChange', attribute='state', old=binfo['state'], new=koji.BUILD_STATES['CANCELED'], info=binfo)
 
 def cancel_build(build_id, cancel_task=True):
@@ -7149,7 +7149,7 @@ def build_notification(task_id, build_id):
         dest_tag = target['dest_tag']
 
     if build['state'] == koji.BUILD_STATES['BUILDING']:
-        raise koji.GenericError, 'never send notifications for incomplete builds'
+        raise koji.GenericError('never send notifications for incomplete builds')
 
     web_url = context.opts.get('KojiWebURL', 'http://localhost/koji')
 
@@ -7169,7 +7169,7 @@ def new_group(name):
     """Add a user group to the database"""
     context.session.assertPerm('admin')
     if get_user(name):
-        raise koji.GenericError, 'user/group already exists: %s' % name
+        raise koji.GenericError('user/group already exists: %s' % name)
     return context.session.createUser(name, usertype=koji.USERTYPES['GROUP'])
 
 def add_group_member(group, user, strict=True):
@@ -7178,9 +7178,9 @@ def add_group_member(group, user, strict=True):
     group = get_user(group)
     user = get_user(user)
     if group['usertype'] != koji.USERTYPES['GROUP']:
-        raise koji.GenericError, "Not a group: %(name)s" % group
+        raise koji.GenericError("Not a group: %(name)s" % group)
     if user['usertype'] == koji.USERTYPES['GROUP']:
-        raise koji.GenericError, "Groups cannot be members of other groups"
+        raise koji.GenericError("Groups cannot be members of other groups")
     #check to see if user is already a member
     data = {'user_id' : user['id'], 'group_id' : group['id']}
     table = 'user_groups'
@@ -7192,7 +7192,7 @@ def add_group_member(group, user, strict=True):
     if row:
         if not strict:
             return
-        raise koji.GenericError, "User already in group"
+        raise koji.GenericError("User already in group")
     insert = InsertProcessor(table, data)
     insert.make_create()
     insert.execute()
@@ -7203,7 +7203,7 @@ def drop_group_member(group, user):
     user = get_user(user, strict=True)
     ginfo = get_user(group)
     if not ginfo or ginfo['usertype'] != koji.USERTYPES['GROUP']:
-        raise koji.GenericError, "No such group: %s" % group
+        raise koji.GenericError("No such group: %s" % group)
     data = {'user_id' : user['id'], 'group_id' : ginfo['id']}
     clauses = ["user_id = %(user_id)i", "group_id = %(group_id)i"]
     update = UpdateProcessor('user_groups', values=data, clauses=clauses)
@@ -7215,7 +7215,7 @@ def get_group_members(group):
     context.session.assertPerm('admin')
     group = get_user(group)
     if group['usertype'] != koji.USERTYPES['GROUP']:
-        raise koji.GenericError, "Not a group: %(name)s" % group
+        raise koji.GenericError("Not a group: %(name)s" % group)
     group_id = group['id']
     fields = ('id', 'name', 'usertype', 'krb_principal')
     q = """SELECT %s FROM user_groups
@@ -7226,7 +7226,7 @@ def get_group_members(group):
 def set_user_status(user, status):
     context.session.assertPerm('admin')
     if not koji.USER_STATUS.get(status):
-        raise koji.GenericError, 'invalid status: %s' % status
+        raise koji.GenericError('invalid status: %s' % status)
     if user['status'] == status:
         # nothing to do
         return
@@ -7235,7 +7235,7 @@ def set_user_status(user, status):
     rows = _dml(update, locals())
     # sanity check
     if rows == 0:
-        raise koji.GenericError, 'invalid user ID: %i' % user_id
+        raise koji.GenericError('invalid user ID: %i' % user_id)
 
 
 def grant_cg_access(user, cg, create=False):
@@ -7502,7 +7502,7 @@ class QueryProcessor(object):
         self.aliases = aliases
         if columns and aliases:
             if len(columns) != len(aliases):
-                raise StandardError, 'column and alias lists must be the same length'
+                raise StandardError('column and alias lists must be the same length')
             self.colsByAlias = dict(zip(aliases, columns))
         else:
             self.colsByAlias = {}
@@ -7608,7 +7608,7 @@ SELECT %(col_str)s
                 elif order in self.columns:
                     orderCol = order
                 else:
-                    raise StandardError, 'invalid order: ' + order
+                    raise StandardError('invalid order: ' + order)
                 order_exprs.append(orderCol + direction)
             return 'ORDER BY ' + ', '.join(order_exprs)
         else:
@@ -7766,20 +7766,20 @@ def policy_get_pkg(data):
             if isinstance(data['package'], basestring):
                 return {'id' : None, 'name' : data['package']}
             else:
-                raise koji.GenericError, "Invalid package: %s" % data['package']
+                raise koji.GenericError("Invalid package: %s" % data['package'])
         return pkginfo
     if data.has_key('build'):
         binfo = get_build(data['build'], strict=True)
         return {'id' : binfo['package_id'], 'name' : binfo['name']}
     #else
-    raise koji.GenericError, "policy requires package data"
+    raise koji.GenericError("policy requires package data")
 
 
 def policy_get_cgs(data):
     """Determine content generators from policy data"""
 
     if not data.has_key('build'):
-        raise koji.GenericError, "policy requires build data"
+        raise koji.GenericError("policy requires build data")
     binfo = get_build(data['build'], strict=True)
 
     # first get buildroots used
@@ -8132,10 +8132,10 @@ class PolicyTest(koji.policy.BaseSimpleTest):
         args = self.str.split()[1:]
         if self.depth != 0:
             #LOOP!
-            raise koji.GenericError, "encountered policy loop at %s" % self.str
+            raise koji.GenericError("encountered policy loop at %s" % self.str)
         ruleset = context.policy.get(args[0])
         if not ruleset:
-            raise koji.GenericError, "no such policy: %s" % args[0]
+            raise koji.GenericError("no such policy: %s" % args[0])
         self.depth += 1
         result = ruleset.apply(data)
         self.depth -= 1
@@ -8191,7 +8191,7 @@ def check_policy(name, data, default='deny', strict=False):
         err_str += ": %s" % reason
     if context.opts.get('KojiDebug') or context.opts.get('VerbosePolicy'):
         err_str += " [rule: %s]" % lastrule
-    raise koji.ActionNotAllowed, err_str
+    raise koji.ActionNotAllowed(err_str)
 
 def assert_policy(name, data, default='deny'):
     """Enforce the named policy
@@ -8260,7 +8260,7 @@ def importImageInternal(task_id, build_id, imgdata):
         archivetype = get_archive_type(imgfile)
         logger.debug('image type we are importing is: %s' % archivetype)
         if not archivetype:
-            raise koji.BuildError, 'Unsupported image type'
+            raise koji.BuildError('Unsupported image type')
         archives.append(import_archive(fullpath, build_info, 'image', imgdata))
 
     # upload logs
@@ -8316,7 +8316,7 @@ class RootExports(object):
     '''Contains functions that are made available via XMLRPC'''
 
     def buildFromCVS(self, url, tag):
-        raise koji.FunctionDeprecated
+        raise koji.FunctionDeprecated()
         #return make_task('buildFromCVS',[url, tag])
 
     def restartHosts(self, priority=5):
@@ -8338,7 +8338,7 @@ class RootExports(object):
         if priority:
             if priority < 0:
                 if not context.session.hasPerm('admin'):
-                    raise koji.ActionNotAllowed, 'only admins may create high-priority tasks'
+                    raise koji.ActionNotAllowed('only admins may create high-priority tasks')
             taskOpts['priority'] = koji.PRIO_DEFAULT + priority
         if channel:
             taskOpts['channel'] = channel
@@ -8363,7 +8363,7 @@ class RootExports(object):
         if priority:
             if priority < 0:
                 if not context.session.hasPerm('admin'):
-                    raise koji.ActionNotAllowed, 'only admins may create high-priority tasks'
+                    raise koji.ActionNotAllowed('only admins may create high-priority tasks')
             taskOpts['priority'] = koji.PRIO_DEFAULT + priority
         if channel:
             taskOpts['channel'] = channel
@@ -8383,14 +8383,14 @@ class RootExports(object):
         Returns the task ID
         """
         if not context.opts.get('EnableMaven'):
-            raise koji.GenericError, "Maven support not enabled"
+            raise koji.GenericError("Maven support not enabled")
         if not opts:
             opts = {}
         taskOpts = {}
         if priority:
             if priority < 0:
                 if not context.session.hasPerm('admin'):
-                    raise koji.ActionNotAllowed, 'only admins may create high-priority tasks'
+                    raise koji.ActionNotAllowed('only admins may create high-priority tasks')
             taskOpts['priority'] = koji.PRIO_DEFAULT + priority
         if channel:
             taskOpts['channel'] = channel
@@ -8413,21 +8413,21 @@ class RootExports(object):
         returns the task ID
         """
         if not context.opts.get('EnableMaven'):
-            raise koji.GenericError, "Maven support not enabled"
+            raise koji.GenericError("Maven support not enabled")
 
         if not opts:
             opts = {}
 
         build = self.getBuild(build, strict=True)
         if list_rpms(build['id']) and not (opts.get('scratch') or opts.get('create_build')):
-            raise koji.PreBuildError, 'wrapper rpms for %s have already been built' % koji.buildLabel(build)
+            raise koji.PreBuildError('wrapper rpms for %s have already been built' % koji.buildLabel(build))
         build_target = self.getBuildTarget(target)
         if not build_target:
-            raise koji.PreBuildError, 'no such build target: %s' % target
+            raise koji.PreBuildError('no such build target: %s' % target)
         build_tag = self.getTag(build_target['build_tag'], strict=True)
         repo_info = self.getRepo(build_tag['id'])
         if not repo_info:
-            raise koji.PreBuildError, 'no repo for tag: %s' % build_tag['name']
+            raise koji.PreBuildError('no repo for tag: %s' % build_tag['name'])
         opts['repo_id'] = repo_info['id']
 
         taskOpts = {}
@@ -8450,12 +8450,12 @@ class RootExports(object):
         Returns the task ID
         """
         if not context.opts.get('EnableMaven'):
-            raise koji.GenericError, "Maven support not enabled"
+            raise koji.GenericError("Maven support not enabled")
         taskOpts = {}
         if priority:
             if priority < 0:
                 if not context.session.hasPerm('admin'):
-                    raise koji.ActionNotAllowed, 'only admins may create high-priority tasks'
+                    raise koji.ActionNotAllowed('only admins may create high-priority tasks')
             taskOpts['priority'] = koji.PRIO_DEFAULT + priority
         if channel:
             taskOpts['channel'] = channel
@@ -8478,7 +8478,7 @@ class RootExports(object):
         Returns the task ID
         """
         if not context.opts.get('EnableWin'):
-            raise koji.GenericError, "Windows support not enabled"
+            raise koji.GenericError("Windows support not enabled")
         targ_info = self.getBuildTarget(target)
         policy_data = {'vm_name': vm,
                        'tag': targ_info['dest_tag']}
@@ -8489,7 +8489,7 @@ class RootExports(object):
         if priority:
             if priority < 0:
                 if not context.session.hasPerm('admin'):
-                    raise koji.ActionNotAllowed, 'only admins may create high-priority tasks'
+                    raise koji.ActionNotAllowed('only admins may create high-priority tasks')
             taskOpts['priority'] = koji.PRIO_DEFAULT + priority
         if channel:
             taskOpts['channel'] = channel
@@ -8504,7 +8504,7 @@ class RootExports(object):
         """
 
         if img_type not in ('livecd', 'appliance', 'livemedia'):
-            raise koji.GenericError, 'Unrecognized image type: %s' % img_type
+            raise koji.GenericError('Unrecognized image type: %s' % img_type)
 
         context.session.assertPerm(img_type)
 
@@ -8516,8 +8516,8 @@ class RootExports(object):
         if priority:
             if priority < 0:
                 if not context.session.hasPerm('admin'):
-                    raise koji.ActionNotAllowed, \
-                               'only admins may create high-priority tasks'
+                    raise koji.ActionNotAllowed(
+                               'only admins may create high-priority tasks')
 
             taskOpts['priority'] = koji.PRIO_DEFAULT + priority
 
@@ -8534,12 +8534,12 @@ class RootExports(object):
         if priority:
             if priority < 0:
                 if not context.session.hasPerm('admin'):
-                    raise koji.ActionNotAllowed, \
-                               'only admins may create high-priority tasks'
+                    raise koji.ActionNotAllowed(
+                               'only admins may create high-priority tasks')
 
             taskOpts['priority'] = koji.PRIO_DEFAULT + priority
         if not opts.has_key('scratch') and not opts.has_key('indirection_template_url'):
-            raise koji.ActionNotAllowed, 'Non-scratch builds must provide url for the indirection template'
+            raise koji.ActionNotAllowed('Non-scratch builds must provide url for the indirection template')
 
         return make_task('indirectionimage', [opts], **taskOpts)
 
@@ -8554,12 +8554,12 @@ class RootExports(object):
         if priority:
             if priority < 0:
                 if not context.session.hasPerm('admin'):
-                    raise koji.ActionNotAllowed, \
-                               'only admins may create high-priority tasks'
+                    raise koji.ActionNotAllowed(
+                               'only admins may create high-priority tasks')
 
             taskOpts['priority'] = koji.PRIO_DEFAULT + priority
         if not opts.has_key('scratch') and not opts.has_key('ksurl'):
-            raise koji.ActionNotAllowed, 'Non-scratch builds must provide ksurl'
+            raise koji.ActionNotAllowed('Non-scratch builds must provide ksurl')
 
         return make_task('image', [name, version, arches, target, inst_tree, opts], **taskOpts)
 
@@ -8570,7 +8570,7 @@ class RootExports(object):
         """
         context.session.assertPerm('admin')
         if not context.opts.get('EnableImageMigration'):
-            raise koji.GenericError, 'Image migration not enabled'
+            raise koji.GenericError('Image migration not enabled')
         old = old_image_data(old_image_id)
         check_old_image_files(old)
         return import_old_image(old, name, version)
@@ -8580,11 +8580,11 @@ class RootExports(object):
 
     def fault(self):
         "debugging. raise an error"
-        raise Exception, "test exception"
+        raise Exception("test exception")
 
     def error(self):
         "debugging. raise an error"
-        raise koji.GenericError, "test error"
+        raise koji.GenericError("test error")
 
     def echo(self, *args):
         return args
@@ -8652,7 +8652,7 @@ class RootExports(object):
         q = """SELECT id, EXTRACT(EPOCH FROM time) FROM events"""
         if before is not None:
             if not isinstance(before, (int, long, float)):
-                raise koji.GenericError, 'invalid type for before: %s' % type(before)
+                raise koji.GenericError('invalid type for before: %s' % type(before))
             # use the repr() conversion because it retains more precision than the
             # string conversion
             q += """ WHERE EXTRACT(EPOCH FROM time) < %(before)r"""
@@ -8708,13 +8708,13 @@ class RootExports(object):
                 raise
         else:
             if not stat.S_ISREG(st.st_mode):
-                raise koji.GenericError, "destination not a file: %s" % fn
+                raise koji.GenericError("destination not a file: %s" % fn)
             elif offset == 0:
                 #first chunk, so file should not exist yet
                 if not fn.endswith('.log'):
                     # but we allow .log files to be uploaded multiple times to support
                     # realtime log-file viewing
-                    raise koji.GenericError, "file already exists: %s" % fn
+                    raise koji.GenericError("file already exists: %s" % fn)
         fd = os.open(fn, os.O_RDWR | os.O_CREAT, 0666)
         # log_error("fd=%r" %fd)
         try:
@@ -8779,17 +8779,17 @@ class RootExports(object):
             try:
                 fcntl.lockf(fd, fcntl.LOCK_SH|fcntl.LOCK_NB)
             except IOError, e:
-                raise koji.LockError, e
+                raise koji.LockError(e)
             st = os.fstat(fd)
             if not stat.S_ISREG(st.st_mode):
-                raise koji.GenericError, "Not a regular file: %s" % fn
+                raise koji.GenericError("Not a regular file: %s" % fn)
             data['size'] = koji.encode_int(st.st_size)
             data['mtime'] = st.st_mtime
             if verify:
                 sum_cls = get_verify_class(verify)
                 if tail is not None:
                     if tail < 0:
-                        raise koji.GenericError, "invalid tail value: %r" % tail
+                        raise koji.GenericError("invalid tail value: %r" % tail)
                     offset = max(st.st_size - tail, 0)
                     os.lseek(fd, offset, 0)
                 length = 0
@@ -8811,11 +8811,11 @@ class RootExports(object):
         """Download the file with the given name, generated by the task with the
         given ID."""
         if '..' in fileName:
-            raise koji.GenericError, 'Invalid file name: %s' % fileName
+            raise koji.GenericError('Invalid file name: %s' % fileName)
         filePath = '%s/%s/%s' % (koji.pathinfo.work(), koji.pathinfo.taskrelpath(taskID), fileName)
         filePath = os.path.normpath(filePath)
         if not os.path.isfile(filePath):
-            raise koji.GenericError, 'no file "%s" output by task %i' % (fileName, taskID)
+            raise koji.GenericError('no file "%s" output by task %i' % (fileName, taskID))
         # Let the caller handler any IO or permission errors
         f = file(filePath, 'r')
         if isinstance(offset, str):
@@ -8871,16 +8871,16 @@ class RootExports(object):
         """
         if type == 'maven':
             if not context.opts.get('EnableMaven'):
-                raise koji.GenericError, 'Maven support not enabled'
+                raise koji.GenericError('Maven support not enabled')
             context.session.assertPerm('maven-import')
         elif type == 'win':
             if not context.opts.get('EnableWin'):
-                raise koji.GenericError, 'Windows support not enabled'
+                raise koji.GenericError('Windows support not enabled')
             context.session.assertPerm('win-import')
         elif type == 'image':
             context.session.assertPerm('image-import')
         else:
-            raise koji.GenericError, 'unsupported archive type: %s' % type
+            raise koji.GenericError('unsupported archive type: %s' % type)
         buildinfo = get_build(buildinfo, strict=True)
         fullpath = '%s/%s' % (koji.pathinfo.work(), filepath)
         import_archive(fullpath, buildinfo, type, typeInfo)
@@ -8919,7 +8919,7 @@ class RootExports(object):
         """
         context.session.assertPerm('maven-import')
         if not context.opts.get('EnableMaven'):
-            raise koji.GenericError, "Maven support not enabled"
+            raise koji.GenericError("Maven support not enabled")
         build = get_build(build_info)
         if not build:
             build_id = new_build(dslice(build_info, ('name', 'version', 'release', 'epoch')))
@@ -8934,7 +8934,7 @@ class RootExports(object):
         """
         context.session.assertPerm('win-import')
         if not context.opts.get('EnableWin'):
-            raise koji.GenericError, "Windows support not enabled"
+            raise koji.GenericError("Windows support not enabled")
         build = get_build(build_info)
         if not build:
             build_id = new_build(dslice(build_info, ('name', 'version', 'release', 'epoch')))
@@ -8962,7 +8962,7 @@ class RootExports(object):
         uploadpath = koji.pathinfo.work()
         fn = "%s/%s/%s" %(uploadpath, path, basename)
         if not os.path.exists(fn):
-            raise koji.GenericError, "No such file: %s" % fn
+            raise koji.GenericError("No such file: %s" % fn)
         rpminfo = import_rpm(fn)
         import_rpm_file(fn, rpminfo['build'], rpminfo)
         add_rpm_sig(rpminfo['id'], koji.rip_rpm_sighdr(fn))
@@ -9051,7 +9051,7 @@ class RootExports(object):
         # build state check
         if build['state'] != koji.BUILD_STATES['COMPLETE']:
             state = koji.BUILD_STATES[build['state']]
-            raise koji.TagError, "build %s not complete: state %s" % (build['nvr'], state)
+            raise koji.TagError("build %s not complete: state %s" % (build['nvr'], state))
         # basic tag access check
         assert_tag_access(tag_id, user_id=None, force=force)
         if fromtag:
@@ -9067,7 +9067,7 @@ class RootExports(object):
             if force and context.session.hasPerm('admin'):
                 pkglist_add(tag_id, pkg_id, force=True, block=False)
             else:
-                raise koji.TagError, pkg_error
+                raise koji.TagError(pkg_error)
         # tag policy check
         policy_data = {'tag' : tag_id, 'build' : build_id, 'fromtag' : fromtag_id}
         if fromtag is None:
@@ -9142,7 +9142,7 @@ class RootExports(object):
             if force and context.session.hasPerm('admin'):
                 pkglist_add(tag2_id, pkg_id, force=True, block=False)
             else:
-                raise koji.TagError, pkg_error
+                raise koji.TagError(pkg_error)
 
         #access check
         assert_tag_access(tag1_id, user_id=None, force=force)
@@ -9228,14 +9228,14 @@ class RootExports(object):
             srpm_path = os.path.join(koji.pathinfo.build(build_info), koji.pathinfo.rpm(srpm_info))
         elif taskID:
             if not filepath:
-                raise koji.GenericError, 'filepath must be spcified with taskID'
+                raise koji.GenericError('filepath must be spcified with taskID')
             if filepath.startswith('/') or '../' in filepath:
-                raise koji.GenericError, 'invalid filepath: %s' % filepath
+                raise koji.GenericError('invalid filepath: %s' % filepath)
             srpm_path = os.path.join(koji.pathinfo.work(),
                                      koji.pathinfo.taskrelpath(taskID),
                                      filepath)
         else:
-            raise koji.GenericError, 'either buildID or taskID and filepath must be specified'
+            raise koji.GenericError('either buildID or taskID and filepath must be specified')
 
         if not os.path.exists(srpm_path):
             return _applyQueryOpts([], queryOpts)
@@ -9248,7 +9248,7 @@ class RootExports(object):
             elif isinstance(before, (int, long)):
                 pass
             else:
-                raise koji.GenericError, 'invalid type for before: %s' % type(before)
+                raise koji.GenericError('invalid type for before: %s' % type(before))
 
         if after:
             if isinstance(after, datetime.datetime):
@@ -9258,7 +9258,7 @@ class RootExports(object):
             elif isinstance(after, (int, long)):
                 pass
             else:
-                raise koji.GenericError, 'invalid type for after: %s' % type(after)
+                raise koji.GenericError('invalid type for after: %s' % type(after))
 
         results = []
 
@@ -9293,7 +9293,7 @@ class RootExports(object):
             return False
         if build['owner_id'] != context.session.user_id:
             if not context.session.hasPerm('admin'):
-                raise koji.ActionNotAllowed, 'Cannot cancel build, not owner'
+                raise koji.ActionNotAllowed('Cannot cancel build, not owner')
         return cancel_build(build['id'])
 
     def assignTask(self, task_id, host, force=False):
@@ -9317,7 +9317,7 @@ class RootExports(object):
         task = Task(task_id)
         if not task.verifyOwner() and not task.verifyHost():
             if not context.session.hasPerm('admin'):
-                raise koji.ActionNotAllowed, 'Cannot cancel task, not owner'
+                raise koji.ActionNotAllowed('Cannot cancel task, not owner')
         #non-admins can also use cancelBuild
         task.cancel(recurse=recurse)
 
@@ -9332,7 +9332,7 @@ class RootExports(object):
         task = Task(task_id)
         if not task.verifyOwner() and not task.verifyHost():
             if not context.session.hasPerm('admin'):
-                raise koji.ActionNotAllowed, 'Cannot cancel task, not owner'
+                raise koji.ActionNotAllowed('Cannot cancel task, not owner')
         task.cancelChildren()
 
     def setTaskPriority(self, task_id, priority, recurse=True):
@@ -9503,7 +9503,7 @@ class RootExports(object):
         else:
             btype = lookup_name('btype', type, strict=False)
             if not btype:
-                raise koji.GenericError, 'unsupported build type: %s' % type
+                raise koji.GenericError('unsupported build type: %s' % type)
             btype_id = btype['id']
             joins.append('build_types ON build.id = build_types.build_id '
                     'AND btype_id = %(btype_id)s')
@@ -9777,14 +9777,14 @@ class RootExports(object):
                 return {}
         elif taskID:
             if not filepath:
-                raise koji.GenericError, 'filepath must be specified with taskID'
+                raise koji.GenericError('filepath must be specified with taskID')
             if filepath.startswith('/') or '../' in filepath:
-                raise koji.GenericError, 'invalid filepath: %s' % filepath
+                raise koji.GenericError('invalid filepath: %s' % filepath)
             rpm_path = os.path.join(koji.pathinfo.work(),
                                     koji.pathinfo.taskrelpath(taskID),
                                     filepath)
         else:
-            raise koji.GenericError, 'either rpmID or taskID and filepath must be specified'
+            raise koji.GenericError('either rpmID or taskID and filepath must be specified')
 
         headers = koji.get_header_fields(rpm_path, headers)
         for key, value in headers.items():
@@ -9929,7 +9929,7 @@ class RootExports(object):
         perm = lookup_perm(permission, strict=(not create), create=create)
         perm_id = perm['id']
         if perm['name'] in koji.auth.get_user_perms(user_id):
-            raise koji.GenericError, 'user %s already has permission: %s' % (userinfo, perm['name'])
+            raise koji.GenericError('user %s already has permission: %s' % (userinfo, perm['name']))
         insert = InsertProcessor('user_perms')
         insert.set(user_id=user_id, perm_id=perm_id)
         insert.make_create()
@@ -9942,7 +9942,7 @@ class RootExports(object):
         perm = lookup_perm(permission, strict=True)
         perm_id = perm['id']
         if perm['name'] not in koji.auth.get_user_perms(user_id):
-            raise koji.GenericError, 'user %s does not have permission: %s' % (userinfo, perm['name'])
+            raise koji.GenericError('user %s does not have permission: %s' % (userinfo, perm['name']))
         update = UpdateProcessor('user_perms', values=locals(),
                     clauses=["user_id = %(user_id)i", "perm_id = %(perm_id)i"])
         update.make_revoke()
@@ -9952,9 +9952,9 @@ class RootExports(object):
         """Add a user to the database"""
         context.session.assertPerm('admin')
         if get_user(username):
-            raise koji.GenericError, 'user already exists: %s' % username
+            raise koji.GenericError('user already exists: %s' % username)
         if krb_principal and get_user(krb_principal):
-            raise koji.GenericError, 'user with this Kerberos principal already exists: %s' % krb_principal
+            raise koji.GenericError('user with this Kerberos principal already exists: %s' % krb_principal)
 
         return context.session.createUser(username, status=status, krb_principal=krb_principal)
 
@@ -9962,14 +9962,14 @@ class RootExports(object):
         """Enable logins by the specified user"""
         user = get_user(username)
         if not user:
-            raise koji.GenericError, 'unknown user: %s' % username
+            raise koji.GenericError('unknown user: %s' % username)
         set_user_status(user, koji.USER_STATUS['NORMAL'])
 
     def disableUser(self, username):
         """Disable logins by the specified user"""
         user = get_user(username)
         if not user:
-            raise koji.GenericError, 'unknown user: %s' % username
+            raise koji.GenericError('unknown user: %s' % username)
         set_user_status(user, koji.USER_STATUS['BLOCKED'])
 
     grantCGAccess = staticmethod(grant_cg_access)
@@ -10084,13 +10084,13 @@ class RootExports(object):
         # This is potentially dangerous, so it must be explicitly enabled
         allowed = context.opts.get('EnableFunctionDebug', False)
         if not allowed:
-            raise koji.ActionNotAllowed, 'This call is not enabled'
+            raise koji.ActionNotAllowed('This call is not enabled')
         context.session.assertPerm('admin')
         func = globals().get(name)
         if callable(func):
             return func(*args, **kwargs)
         else:
-            raise koji.GenericError, 'Unable to find function: %s' % name
+            raise koji.GenericError('Unable to find function: %s' % name)
 
     tagChangedSinceEvent = staticmethod(tag_changed_since_event)
     createBuildTarget = staticmethod(create_build_target)
@@ -10330,12 +10330,12 @@ class RootExports(object):
         The logged-in user must be the owner of the original task or an admin."""
         task = Task(taskID)
         if not (task.isCanceled() or task.isFailed()):
-            raise koji.GenericError, 'only canceled or failed tasks may be resubmitted'
+            raise koji.GenericError('only canceled or failed tasks may be resubmitted')
         taskInfo = task.getInfo()
         if taskInfo['parent'] != None:
-            raise koji.GenericError, 'only top-level tasks may be resubmitted'
+            raise koji.GenericError('only top-level tasks may be resubmitted')
         if not (context.session.user_id == taskInfo['owner'] or self.hasPerm('admin')):
-            raise koji.GenericError, 'only the task owner or an admin may resubmit a task'
+            raise koji.GenericError('only the task owner or an admin may resubmit a task')
 
         args = task.getRequest()
         channel = get_channel(taskInfo['channel_id'], strict=True)
@@ -10346,7 +10346,7 @@ class RootExports(object):
         """Add a host to the database"""
         context.session.assertPerm('admin')
         if get_host(hostname):
-            raise koji.GenericError, 'host already exists: %s' % hostname
+            raise koji.GenericError('host already exists: %s' % hostname)
         q = """SELECT id FROM channels WHERE name = 'default'"""
         default_channel = _singleValue(q)
         if krb_principal is None:
@@ -10490,10 +10490,10 @@ class RootExports(object):
         context.session.assertPerm('admin')
         buildinfo = get_build(build)
         if not buildinfo:
-            raise koji.GenericError, 'build does not exist: %s' % build
+            raise koji.GenericError('build does not exist: %s' % build)
         userinfo = get_user(user)
         if not userinfo:
-            raise koji.GenericError, 'user does not exist: %s' % user
+            raise koji.GenericError('user does not exist: %s' % user)
         userid = userinfo['id']
         buildid = buildinfo['id']
         koji.plugin.run_callbacks('preBuildStateChange', attribute='owner_id', old=buildinfo['owner_id'], new=userid, info=buildinfo)
@@ -10510,16 +10510,16 @@ class RootExports(object):
         context.session.assertPerm('admin')
         buildinfo = get_build(build)
         if not buildinfo:
-            raise koji.GenericError, 'build does not exist: %s' % build
+            raise koji.GenericError('build does not exist: %s' % build)
         elif isinstance(ts, xmlrpclib.DateTime):
             #not recommended
             #the xmlrpclib.DateTime class is almost useless
             try:
                 ts = time.mktime(time.strptime(str(ts), '%Y%m%dT%H:%M:%S'))
             except ValueError:
-                raise koji.GenericError, "Invalid time: %s" % ts
+                raise koji.GenericError("Invalid time: %s" % ts)
         elif not isinstance(ts, (int, long, float)):
-            raise koji.GenericError, "Invalid type for timestamp"
+            raise koji.GenericError("Invalid type for timestamp")
         koji.plugin.run_callbacks('preBuildStateChange', attribute='completion_ts', old=buildinfo['completion_ts'], new=ts, info=buildinfo)
         buildid = buildinfo['id']
         q = """UPDATE build
@@ -10608,7 +10608,7 @@ class RootExports(object):
             _count = 1
 
         if not isinstance(results, list):
-            raise TypeError, '%s() did not return a list' % methodName
+            raise TypeError('%s() did not return a list' % methodName)
 
         order = filterOpts.get('order')
         if order:
@@ -10631,7 +10631,7 @@ class RootExports(object):
         if userID is None:
             user = self.getLoggedInUser()
             if user is None:
-                raise koji.GenericError, 'not logged-in'
+                raise koji.GenericError('not logged-in')
             else:
                 userID = user['id']
         return get_build_notifications(userID)
@@ -10652,15 +10652,15 @@ class RootExports(object):
         owner or the notification or an admin, raise a GenericError."""
         currentUser = self.getLoggedInUser()
         if not currentUser:
-            raise koji.GenericError, 'not logged-in'
+            raise koji.GenericError('not logged-in')
 
         orig_notif = self.getBuildNotification(id)
         if not orig_notif:
-            raise koji.GenericError, 'no notification with ID: %i' % id
+            raise koji.GenericError('no notification with ID: %i' % id)
         elif not (orig_notif['user_id'] == currentUser['id'] or
                   self.hasPerm('admin')):
-            raise koji.GenericError, 'user %i cannot update notifications for user %i' % \
-                  (currentUser['id'], orig_notif['user_id'])
+            raise koji.GenericError('user %i cannot update notifications for user %i' % \
+                  (currentUser['id'], orig_notif['user_id']))
 
         update = """UPDATE build_notifications
         SET package_id = %(package_id)s,
@@ -10676,15 +10676,15 @@ class RootExports(object):
         and the currently logged-in user is not an admin, raise a GenericError."""
         currentUser = self.getLoggedInUser()
         if not currentUser:
-            raise koji.GenericError, 'not logged in'
+            raise koji.GenericError('not logged in')
 
         notificationUser = self.getUser(user_id)
         if not notificationUser:
-            raise koji.GenericError, 'invalid user ID: %s' % user_id
+            raise koji.GenericError('invalid user ID: %s' % user_id)
 
         if not (notificationUser['id'] == currentUser['id'] or self.hasPerm('admin')):
-            raise koji.GenericError, 'user %s cannot create notifications for user %s' % \
-                  (currentUser['name'], notificationUser['name'])
+            raise koji.GenericError('user %s cannot create notifications for user %s' % \
+                  (currentUser['name'], notificationUser['name']))
 
         email = '%s@%s' % (notificationUser['name'], context.opts['EmailDomain'])
         insert = """INSERT INTO build_notifications
@@ -10699,15 +10699,15 @@ class RootExports(object):
         user is not the owner of the notification or an admin, raise a GenericError."""
         notification = self.getBuildNotification(id)
         if not notification:
-            raise koji.GenericError, 'no notification with ID: %i' % id
+            raise koji.GenericError('no notification with ID: %i' % id)
         currentUser = self.getLoggedInUser()
         if not currentUser:
-            raise koji.GenericError, 'not logged-in'
+            raise koji.GenericError('not logged-in')
 
         if not (notification['user_id'] == currentUser['id'] or
                 self.hasPerm('admin')):
-            raise koji.GenericError, 'user %i cannot delete notifications for user %i' % \
-                  (currentUser['id'], notification['user_id'])
+            raise koji.GenericError('user %i cannot delete notifications for user %i' % \
+                  (currentUser['id'], notification['user_id']))
         delete = """DELETE FROM build_notifications WHERE id = %(id)i"""
         _dml(delete, locals())
 
@@ -10742,13 +10742,13 @@ class RootExports(object):
         "name" will be returned.  If no matches are found, an empty
         list will be returned."""
         if not terms:
-            raise koji.GenericError, 'empty search terms'
+            raise koji.GenericError('empty search terms')
         if type == 'file':
             # searching by filename is no longer supported
             return _applyQueryOpts([], queryOpts)
         table = self._searchTables.get(type)
         if not table:
-            raise koji.GenericError, 'unknown search type: %s' % type
+            raise koji.GenericError('unknown search type: %s' % type)
 
         if matchType == 'glob':
             oper = 'ilike'
@@ -10823,7 +10823,7 @@ class BuildRoot(object):
                     values={'id': id}, clauses=['id=%(id)s'])
         data = query.executeOne()
         if not data:
-            raise koji.GenericError, 'no buildroot with ID: %i' % id
+            raise koji.GenericError('no buildroot with ID: %i' % id)
         self.id = id
         self.data = data
         if data['br_type'] == koji.BR_TYPES['STANDARD']:
@@ -10905,8 +10905,8 @@ class BuildRoot(object):
     def assertTask(self, task_id):
         self.assertStandard()
         if not self.verifyTask(task_id):
-            raise koji.ActionNotAllowed, 'Task %s does not have lock on buildroot %s' \
-                                        %(task_id, self.id)
+            raise koji.ActionNotAllowed('Task %s does not have lock on buildroot %s' \
+                                        % (task_id, self.id))
 
     def verifyHost(self, host_id):
         self.assertStandard()
@@ -10915,8 +10915,8 @@ class BuildRoot(object):
     def assertHost(self, host_id):
         self.assertStandard()
         if not self.verifyHost(host_id):
-            raise koji.ActionNotAllowed, "Host %s not owner of buildroot %s" \
-                                        % (host_id, self.id)
+            raise koji.ActionNotAllowed("Host %s not owner of buildroot %s" \
+                                        % (host_id, self.id))
 
     def setState(self, state):
         self.assertStandard()
@@ -10925,7 +10925,7 @@ class BuildRoot(object):
         #sanity checks
         if state == koji.BR_STATES['INIT']:
             #we do not re-init buildroots
-            raise koji.GenericError, "Cannot change buildroot state to INIT"
+            raise koji.GenericError("Cannot change buildroot state to INIT")
         query = QueryProcessor(columns=['state', 'retire_event'], values=self.data,
                     tables=['standard_buildroot'], clauses=['buildroot_id=%(id)s'],
                     opts={'rowlock':True})
@@ -10939,7 +10939,7 @@ class BuildRoot(object):
             if state == 'EXPIRED':
                 return
             else:
-                raise koji.GenericError, "buildroot %i is EXPIRED" % self.id
+                raise koji.GenericError("buildroot %i is EXPIRED" % self.id)
         update = UpdateProcessor('standard_buildroot', clauses=['buildroot_id=%(id)s'],
                                  values=self.data)
         update.set(state=state)
@@ -10950,7 +10950,7 @@ class BuildRoot(object):
 
     def getList(self):
         if self.id is None:
-            raise koji.GenericError, "buildroot not specified"
+            raise koji.GenericError("buildroot not specified")
         brootid = self.id
         fields = (
             ('rpm_id', 'rpm_id'),
@@ -10975,7 +10975,7 @@ class BuildRoot(object):
         """Set or update the list of rpms in a buildroot"""
 
         if self.id is None:
-            raise koji.GenericError, "buildroot not specified"
+            raise koji.GenericError("buildroot not specified")
         if update:
             current = set([r['rpm_id'] for r in self.getList()])
         rpm_ids = []
@@ -11005,13 +11005,13 @@ class BuildRoot(object):
         """Set the initial list of rpms in a buildroot"""
 
         if self.is_standard and self.data['state'] != koji.BR_STATES['INIT']:
-            raise koji.GenericError, "buildroot %(id)s in wrong state %(state)s" % self.data
+            raise koji.GenericError("buildroot %(id)s in wrong state %(state)s" % self.data)
         self._setList(rpmlist, update=False)
 
     def updateList(self, rpmlist):
         """Update the list of packages in a buildroot"""
         if self.is_standard and self.data['state'] != koji.BR_STATES['BUILDING']:
-            raise koji.GenericError, "buildroot %(id)s in wrong state %(state)s" % self.data
+            raise koji.GenericError("buildroot %(id)s in wrong state %(state)s" % self.data)
         self._setList(rpmlist, update=True)
 
     def getArchiveList(self, queryOpts=None):
@@ -11045,9 +11045,9 @@ class BuildRoot(object):
 
         if self.is_standard:
             if not (context.opts.get('EnableMaven') or context.opts.get('EnableWin')):
-                raise koji.GenericError, "non-rpm support is not enabled"
+                raise koji.GenericError("non-rpm support is not enabled")
             if self.data['state'] != koji.BR_STATES['BUILDING']:
-                raise koji.GenericError, "buildroot %(id)s in wrong state %(state)s" % self.data
+                raise koji.GenericError("buildroot %(id)s in wrong state %(state)s" % self.data)
         archives = set([r['id'] for r in archives])
         current = set([r['id'] for r in self.getArchiveList()])
         new_archives = archives.difference(current)
@@ -11076,18 +11076,18 @@ class Host(object):
             id = remote_id
         if id is None:
             if context.session.logged_in:
-                raise koji.AuthError, "User %i is not a host" % context.session.user_id
+                raise koji.AuthError("User %i is not a host" % context.session.user_id)
             else:
-                raise koji.AuthError, "Not logged in"
+                raise koji.AuthError("Not logged in")
         self.id = id
         self.same_host = (id == remote_id)
 
     def verify(self):
         """Verify that the remote host matches and has the lock"""
         if not self.same_host:
-            raise koji.AuthError, "Host mismatch"
+            raise koji.AuthError("Host mismatch")
         if not context.session.exclusive:
-            raise koji.AuthError, "This method requires an exclusive session"
+            raise koji.AuthError("This method requires an exclusive session")
         return True
 
     def taskUnwait(self, parent):
@@ -11184,7 +11184,7 @@ class Host(object):
             c.execute(q, locals())
             for task_id, state in c.fetchall():
                 if state == canceled:
-                    raise koji.GenericError, "Subtask canceled"
+                    raise koji.GenericError("Subtask canceled")
                 elif state in (closed, failed):
                     tasks.append(task_id)
         # Would use a dict, but xmlrpc requires the keys to be strings
@@ -11425,7 +11425,7 @@ class HostExports(object):
         for relpath in [srpm] + rpms:
             fn = "%s/%s" % (uploadpath, relpath)
             if not os.path.exists(fn):
-                raise koji.GenericError, "no such file: %s" % fn
+                raise koji.GenericError("no such file: %s" % fn)
 
         rpms = check_noarch_rpms(uploadpath, rpms)
 
@@ -11456,7 +11456,7 @@ class HostExports(object):
     def moveMavenBuildToScratch(self, task_id, results, rpm_results):
         "Move a completed Maven scratch build into place (not imported)"
         if not context.opts.get('EnableMaven'):
-            raise koji.GenericError, 'Maven support not enabled'
+            raise koji.GenericError('Maven support not enabled')
         host = Host()
         host.verify()
         task = Task(task_id)
@@ -11488,7 +11488,7 @@ class HostExports(object):
     def moveWinBuildToScratch(self, task_id, results, rpm_results):
         "Move a completed Windows scratch build into place (not imported)"
         if not context.opts.get('EnableWin'):
-            raise koji.GenericError, 'Windows support not enabled'
+            raise koji.GenericError('Windows support not enabled')
         host = Host()
         host.verify()
         task = Task(task_id)
@@ -11608,7 +11608,7 @@ class HostExports(object):
            Synthesize the release number by taking the (integer) release of the
            last successful build and incrementing it."""
         if not context.opts.get('EnableMaven'):
-            raise koji.GenericError, "Maven support not enabled"
+            raise koji.GenericError("Maven support not enabled")
         host = Host()
         host.verify()
         task = Task(task_id)
@@ -11633,13 +11633,13 @@ class HostExports(object):
         host = Host()
         host.verify()
         if not context.opts.get('EnableMaven'):
-            raise koji.GenericError, "Maven support not enabled"
+            raise koji.GenericError("Maven support not enabled")
         new_maven_build(build_info, maven_info)
 
     def completeMavenBuild(self, task_id, build_id, maven_results, rpm_results):
         """Complete the Maven build."""
         if not context.opts.get('EnableMaven'):
-            raise koji.GenericError, "Maven support not enabled"
+            raise koji.GenericError("Maven support not enabled")
         host = Host()
         host.verify()
         task = Task(task_id)
@@ -11665,7 +11665,7 @@ class HostExports(object):
                 pom_info = koji.parse_pom(pom_path)
                 dir_maven_info = koji.pom_to_maven_info(pom_info)
             else:
-                raise koji.BuildError, 'multiple .pom files in %s: %s' % (relpath, ', '.join(poms))
+                raise koji.BuildError('multiple .pom files in %s: %s' % (relpath, ', '.join(poms)))
 
             for filename in files:
                 if os.path.splitext(filename)[1] in ('.md5', '.sha1'):
@@ -11685,7 +11685,7 @@ class HostExports(object):
                 archivetype = get_archive_type(filename)
                 if not archivetype:
                     # Unknown archive type, fail the build
-                    raise koji.BuildError, 'unsupported file type: %s' % filename
+                    raise koji.BuildError('unsupported file type: %s' % filename)
                 import_archive(filepath, build_info, 'maven', dir_maven_info, maven_buildroot_id)
 
         # move the logs to their final destination
@@ -11718,19 +11718,19 @@ class HostExports(object):
         host.verify()
         if type == 'maven':
             if not context.opts.get('EnableMaven'):
-                raise koji.GenericError, 'Maven support not enabled'
+                raise koji.GenericError('Maven support not enabled')
         elif type == 'win':
             if not context.opts.get('EnableWin'):
-                raise koji.GenericError, 'Windows support not enabled'
+                raise koji.GenericError('Windows support not enabled')
         else:
-            raise koji.GenericError, 'unsupported archive type: %s' % type
+            raise koji.GenericError('unsupported archive type: %s' % type)
         import_archive(filepath, buildinfo, type, typeInfo)
 
     def importWrapperRPMs(self, task_id, build_id, rpm_results):
         """Import the wrapper rpms and associate them with the given build.  The build
         must not have any existing rpms associated with it."""
         if not context.opts.get('EnableMaven'):
-            raise koji.GenericError, "Maven support not enabled"
+            raise koji.GenericError("Maven support not enabled")
         host = Host()
         host.verify()
         task = Task(task_id)
@@ -11739,12 +11739,12 @@ class HostExports(object):
         build_info = get_build(build_id, strict=True)
 
         if build_info['state'] != koji.BUILD_STATES['COMPLETE']:
-            raise koji.GenericError, 'cannot import wrapper rpms for %s: build state is %s, not complete' % \
-                (koji.buildLabel(build_info), koji.BUILD_STATES[build_info['state']].lower())
+            raise koji.GenericError('cannot import wrapper rpms for %s: build state is %s, not complete' % \
+                (koji.buildLabel(build_info), koji.BUILD_STATES[build_info['state']].lower()))
 
         if list_rpms(buildID=build_info['id']):
             # don't allow overwriting of already-imported wrapper RPMs
-            raise koji.GenericError, 'wrapper rpms for %s have already been imported' % koji.buildLabel(build_info)
+            raise koji.GenericError('wrapper rpms for %s have already been imported' % koji.buildLabel(build_info))
 
         _import_wrapper(task.id, build_info, rpm_results)
 
@@ -11769,7 +11769,7 @@ class HostExports(object):
         Create a new in-progress Windows build.
         """
         if not context.opts.get('EnableWin'):
-            raise koji.GenericError, 'Windows support not enabled'
+            raise koji.GenericError('Windows support not enabled')
         host = Host()
         host.verify()
         #sanity checks
@@ -11789,7 +11789,7 @@ class HostExports(object):
     def completeWinBuild(self, task_id, build_id, results, rpm_results):
         """Complete a Windows build"""
         if not context.opts.get('EnableWin'):
-            raise koji.GenericError, 'Windows support not enabled'
+            raise koji.GenericError('Windows support not enabled')
         host = Host()
         host.verify()
         task = Task(task_id)
@@ -11804,7 +11804,7 @@ class HostExports(object):
             archivetype = get_archive_type(relpath)
             if not archivetype:
                 # Unknown archive type, fail the build
-                raise koji.BuildError, 'unsupported file type: %s' % relpath
+                raise koji.BuildError('unsupported file type: %s' % relpath)
             filepath = os.path.join(task_dir, relpath)
             metadata['relpath'] = os.path.dirname(relpath)
             import_archive(filepath, build_info, 'win', metadata, buildroot_id=results['buildroot_id'])
@@ -11854,11 +11854,11 @@ class HostExports(object):
         result = _singleRow(query, locals(), ('state', 'completion_time'))
 
         if result['state'] != koji.BUILD_STATES['BUILDING']:
-            raise koji.GenericError, 'cannot update build %i, state: %s' % \
-                  (build_id, koji.BUILD_STATES[result['state']])
+            raise koji.GenericError('cannot update build %i, state: %s' % \
+                  (build_id, koji.BUILD_STATES[result['state']]))
         elif result['completion_time'] is not None:
-            raise koji.GenericError, 'cannot update build %i, completed at %s' % \
-                  (build_id, result['completion_time'])
+            raise koji.GenericError('cannot update build %i, completed at %s' % \
+                  (build_id, result['completion_time']))
 
         update = """UPDATE build
         SET state = %(st_failed)i,
@@ -11935,7 +11935,7 @@ class HostExports(object):
         host.verify()
         ruleset = context.policy.get(name)
         if not ruleset:
-            raise koji.GenericError, "no such policy: %s" % name
+            raise koji.GenericError("no such policy: %s" % name)
         return ruleset.apply(data)
 
     def newBuildRoot(self, repo, arch, task_id=None):
@@ -11991,7 +11991,7 @@ class HostExports(object):
     def updateMavenBuildRootList(self, brootid, task_id, mavenlist, ignore=None, project=False,
                                  ignore_unknown=False, extra_deps=None):
         if not context.opts.get('EnableMaven'):
-            raise koji.GenericError, "Maven support not enabled"
+            raise koji.GenericError("Maven support not enabled")
         host = Host()
         host.verify()
         Task(task_id).assertHost(host.id)
@@ -12115,8 +12115,8 @@ class HostExports(object):
                             logger.error("Build supplies %i archives: %r", len(build_archives), build_archives.keys())
                         if tag_archive:
                             logger.error("Size mismatch, br: %i, db: %i", fileinfo['size'], tag_archive['size'])
-                        raise koji.BuildrootError, 'Unknown file in build environment: %s, size: %s' % \
-                              ('%s/%s' % (fileinfo['path'], fileinfo['filename']), fileinfo['size'])
+                        raise koji.BuildrootError('Unknown file in build environment: %s, size: %s' % \
+                              ('%s/%s' % (fileinfo['path'], fileinfo['filename']), fileinfo['size']))
 
         return br.updateArchiveList(archives, project)
 
@@ -12133,12 +12133,12 @@ class HostExports(object):
         rinfo = repo_info(repo_id, strict=True)
         repodir = koji.pathinfo.repo(repo_id, rinfo['tag_name'])
         if rinfo['state'] != koji.REPO_INIT:
-            raise koji.GenericError, "Repo %(id)s not in INIT state (got %(state)s)" % rinfo
+            raise koji.GenericError("Repo %(id)s not in INIT state (got %(state)s)" % rinfo)
         #verify file exists
         uploadpath = koji.pathinfo.work()
         filepath = "%s/%s" % (uploadpath, path)
         if not os.path.exists(filepath):
-            raise koji.GenericError, "no such file: %s" % filepath
+            raise koji.GenericError("no such file: %s" % filepath)
         rpminfo = koji.get_header_fields(filepath, ('arch', 'sourcepackage'))
         dirs = []
         if not rpminfo['sourcepackage'] and rpminfo['arch'] != 'noarch':
@@ -12164,7 +12164,7 @@ class HostExports(object):
                 s_st = os.stat(filepath)
                 d_st = os.stat(dst)
                 if s_st.st_ino != d_st.st_ino:
-                    raise koji.GenericError, "File already in repo: %s" % dst
+                    raise koji.GenericError("File already in repo: %s" % dst)
                 #otherwise the desired hardlink already exists
             else:
                 safer_move(filepath, dst)
@@ -12183,20 +12183,20 @@ class HostExports(object):
         rinfo = repo_info(repo_id, strict=True)
         koji.plugin.run_callbacks('preRepoDone', repo=rinfo, data=data, expire=expire)
         if rinfo['state'] != koji.REPO_INIT:
-            raise koji.GenericError, "Repo %(id)s not in INIT state (got %(state)s)" % rinfo
+            raise koji.GenericError("Repo %(id)s not in INIT state (got %(state)s)" % rinfo)
         repodir = koji.pathinfo.repo(repo_id, rinfo['tag_name'])
         workdir = koji.pathinfo.work()
         for arch, (uploadpath, files) in data.iteritems():
             archdir = "%s/%s" % (repodir, arch)
             if not os.path.isdir(archdir):
-                raise koji.GenericError, "Repo arch directory missing: %s" % archdir
+                raise koji.GenericError("Repo arch directory missing: %s" % archdir)
             datadir = "%s/repodata" % archdir
             koji.ensuredir(datadir)
             for fn in files:
                 src = "%s/%s/%s" % (workdir, uploadpath, fn)
                 dst = "%s/%s" % (datadir, fn)
                 if not os.path.exists(src):
-                    raise koji.GenericError, "uploaded file missing: %s" % src
+                    raise koji.GenericError("uploaded file missing: %s" % src)
                 safer_move(src, dst)
         if expire:
             repo_expire(repo_id)
@@ -12229,19 +12229,19 @@ def get_upload_path(reldir, name, create=False):
     # lots of sanity checks
     d, name = os.path.split(name)
     if d or name.startswith('.'):
-        raise koji.GenericError, "Invalid upload filename: %s" % orig_name
+        raise koji.GenericError("Invalid upload filename: %s" % orig_name)
     reldir = os.path.normpath(reldir)
     if not reldir or reldir.startswith('..'):
-        raise koji.GenericError, "Invalid upload directory: %s" % orig_reldir
+        raise koji.GenericError("Invalid upload directory: %s" % orig_reldir)
     parts = reldir.split('/')
     check_user = True
     if create and parts[0] == "tasks":
         if len(parts) < 3:
-            raise koji.GenericError, "Invalid task upload directory: %s" % orig_reldir
+            raise koji.GenericError("Invalid task upload directory: %s" % orig_reldir)
         try:
             task_id = int(parts[2])
         except ValueError:
-            raise koji.GenericError, "Invalid task upload directory: %s" % orig_reldir
+            raise koji.GenericError("Invalid task upload directory: %s" % orig_reldir)
         # only the host running this task may write here
         host = Host()
         host.verify()
@@ -12256,7 +12256,7 @@ def get_upload_path(reldir, name, create=False):
             if os.path.exists(u_fn):
                 user_id = int(file(u_fn, 'r').read())
                 if context.session.user_id != user_id:
-                    raise koji.GenericError, "Invalid upload directory, not owner: %s" % orig_reldir
+                    raise koji.GenericError("Invalid upload directory, not owner: %s" % orig_reldir)
             else:
                 fo = file(u_fn, 'w')
                 fo.write(str(context.session.user_id))
@@ -12269,7 +12269,7 @@ def get_verify_class(verify):
     elif verify == 'adler32':
         return koji.util.adler32_constructor
     elif verify:
-        raise koji.GenericError, "Unsupported verify type: %s" % verify
+        raise koji.GenericError("Unsupported verify type: %s" % verify)
     else:
         return None
 
@@ -12279,7 +12279,7 @@ def handle_upload(environ):
     logger = logging.getLogger('koji.upload')
     start = time.time()
     if not context.session.logged_in:
-        raise koji.ActionNotAllowed, 'you must be logged-in to upload a file'
+        raise koji.ActionNotAllowed('you must be logged-in to upload a file')
     args = cgi.parse_qs(environ.get('QUERY_STRING', ''), strict_parsing=True)
     #XXX - already parsed by auth
     name = args['filename'][0]
@@ -12291,9 +12291,9 @@ def handle_upload(environ):
     fn = get_upload_path(path, name, create=True)
     if os.path.exists(fn):
         if not os.path.isfile(fn):
-            raise koji.GenericError, "destination not a file: %s" % fn
+            raise koji.GenericError("destination not a file: %s" % fn)
         if offset == 0 and not overwrite:
-            raise koji.GenericError, "upload path exists: %s" % fn
+            raise koji.GenericError("upload path exists: %s" % fn)
     sum_cls = get_verify_class(verify)
     size = 0
     chksum = sum_cls()
@@ -12303,7 +12303,7 @@ def handle_upload(environ):
         try:
             fcntl.lockf(fd, fcntl.LOCK_EX|fcntl.LOCK_NB)
         except IOError, e:
-            raise koji.LockError, e
+            raise koji.LockError(e)
         if offset == -1:
             offset = os.lseek(fd, 0, 2)
         else:
diff --git a/hub/kojixmlrpc.py b/hub/kojixmlrpc.py
index 47c1284..634e8e0 100644
--- a/hub/kojixmlrpc.py
+++ b/hub/kojixmlrpc.py
@@ -178,7 +178,7 @@ class HandlerRegistry(object):
     def get(self, name):
         func = self.funcs.get(name, None)
         if func is None:
-            raise koji.GenericError, "Invalid method: %s" % name
+            raise koji.GenericError("Invalid method: %s" % name)
         return func
 
 
@@ -221,7 +221,7 @@ class ModXMLRPCRequestHandler(object):
                 break
             rlen += len(chunk)
             if maxlen and rlen > maxlen:
-                raise koji.GenericError, 'Request too long'
+                raise koji.GenericError('Request too long')
             parser.feed(chunk)
         parser.close()
         return unmarshaller.close(), unmarshaller.getmethodname()
@@ -292,7 +292,7 @@ class ModXMLRPCRequestHandler(object):
         if context.opts.get('LockOut') and \
             context.method not in ('login', 'krbLogin', 'sslLogin', 'logout') and \
             not context.session.hasPerm('admin'):
-            raise koji.ServerOffline, "Server disabled for maintenance"
+            raise koji.ServerOffline("Server disabled for maintenance")
 
     def _dispatch(self, method, params):
         func = self._get_handler(method)
@@ -639,7 +639,7 @@ def setup_logging2(opts):
             level = part
             default = level
         if level not in valid_levels:
-            raise koji.GenericError, "Invalid log level: %s" % level
+            raise koji.GenericError("Invalid log level: %s" % level)
         #all our loggers start with koji
         if name == '':
             name = 'koji'
diff --git a/koji/__init__.py b/koji/__init__.py
index d33415b..8a9bafa 100644
--- a/koji/__init__.py
+++ b/koji/__init__.py
@@ -443,7 +443,7 @@ def decode_args2(args, names, strict=True):
     "An alternate form of decode_args, returns a dictionary"
     args, opts = decode_args(*args)
     if strict and len(names) < len(args):
-        raise TypeError, "Expecting at most %i arguments" % len(names)
+        raise TypeError("Expecting at most %i arguments" % len(names))
     ret = dict(zip(names, args))
     ret.update(opts)
     return ret
@@ -480,13 +480,13 @@ def ensuredir(directory):
     """Create directory, if necessary."""
     if os.path.exists(directory):
         if not os.path.isdir(directory):
-            raise OSError, "Not a directory: %s" % directory
+            raise OSError("Not a directory: %s" % directory)
     else:
         head, tail = os.path.split(directory)
         if not tail and head == directory:
             # can only happen if directory == '/' or equivalent
             # (which obviously should not happen)
-            raise OSError, "root directory missing? %s" % directory
+            raise OSError("root directory missing? %s" % directory)
         if head:
             ensuredir(head)
         # note: if head is blank, then we've reached the top of a relative path
@@ -555,7 +555,7 @@ def rpm_hdr_size(f, ofs=None):
         fo.seek(ofs, 0)
     magic = fo.read(3)
     if magic != RPM_HEADER_MAGIC:
-        raise GenericError, "Invalid rpm: bad magic: %r" % magic
+        raise GenericError("Invalid rpm: bad magic: %r" % magic)
 
     # skip past section magic and such
     #   (3 bytes magic, 1 byte version number, 4 bytes reserved)
@@ -588,7 +588,7 @@ class RawHeader(object):
 
     def __init__(self, data):
         if data[0:3] != RPM_HEADER_MAGIC:
-            raise GenericError, "Invalid rpm header: bad magic: %r" % (data[0:3],)
+            raise GenericError("Invalid rpm header: bad magic: %r" % (data[0:3],))
         self.header = data
         self._index()
 
@@ -726,7 +726,7 @@ class RawHeader(object):
             return self.header[pos:pos+count]
         else:
             #XXX - not all valid data types are handled
-            raise GenericError, "Unable to read header data type: %x" % dtype
+            raise GenericError("Unable to read header data type: %x" % dtype)
 
     def get(self, key, default=None):
         entry = self.index.get(key)
@@ -760,7 +760,7 @@ def __parse_packet_header(pgp_packet):
     """Parse pgp_packet header, return tag type and the rest of pgp_packet"""
     byte0 = ord(pgp_packet[0])
     if (byte0 & 0x80) == 0:
-        raise ValueError, 'Not an OpenPGP packet'
+        raise ValueError('Not an OpenPGP packet')
     if (byte0 & 0x40) == 0:
         tag = (byte0 & 0x3C) >> 2
         len_type = byte0 & 0x03
@@ -784,10 +784,10 @@ def __parse_packet_header(pgp_packet):
             offset = 6
         else:
             # Who the ... would use partial body lengths in a signature packet?
-            raise NotImplementedError, \
-                'OpenPGP packet with partial body lengths'
+            raise NotImplementedError(
+                'OpenPGP packet with partial body lengths')
     if len(pgp_packet) != offset + length:
-        raise ValueError, 'Invalid OpenPGP packet length'
+        raise ValueError('Invalid OpenPGP packet length')
     return (tag, pgp_packet[offset:])
 
 def __subpacket_key_ids(subs):
@@ -813,7 +813,7 @@ def get_sigpacket_key_id(sigpacket):
     """Return ID of the key used to create sigpacket as a hexadecimal string"""
     (tag, sigpacket) = __parse_packet_header(sigpacket)
     if tag != 2:
-        raise ValueError, 'Not a signature packet'
+        raise ValueError('Not a signature packet')
     if ord(sigpacket[0]) == 0x03:
         key_id = sigpacket[11:15]
     elif ord(sigpacket[0]) == 0x04:
@@ -824,12 +824,12 @@ def get_sigpacket_key_id(sigpacket):
         off += 2
         key_ids += __subpacket_key_ids(sigpacket[off : off+sub_len])
         if len(key_ids) != 1:
-            raise NotImplementedError, \
-                'Unexpected number of key IDs: %s' % len(key_ids)
+            raise NotImplementedError(
+                'Unexpected number of key IDs: %s' % len(key_ids))
         key_id = key_ids[0][-4:]
     else:
-        raise NotImplementedError, \
-            'Unknown PGP signature packet version %s' % ord(sigpacket[0])
+        raise NotImplementedError(
+            'Unknown PGP signature packet version %s' % ord(sigpacket[0]))
     return hex_string(key_id)
 
 def get_sighdr_key(sighdr):
@@ -886,7 +886,7 @@ def get_header_field(hdr, name):
         return []
     idx = getattr(rpm, "RPMTAG_%s" % name.upper(), None)
     if idx is None:
-        raise GenericError, "No such rpm header field: %s" % name
+        raise GenericError("No such rpm header field: %s" % name)
     return hdr[idx]
 
 def get_header_fields(X, fields):
@@ -1091,7 +1091,7 @@ def parse_pom(path=None, contents=None):
         fd.close()
 
     if not contents:
-        raise GenericError, 'either a path to a pom file or the contents of a pom file must be specified'
+        raise GenericError('either a path to a pom file or the contents of a pom file must be specified')
 
     # A common problem is non-UTF8 characters in XML files, so we'll convert the string first
 
@@ -1108,7 +1108,7 @@ def parse_pom(path=None, contents=None):
 
     for field in fields:
         if field not in values.keys():
-            raise GenericError, 'could not extract %s from POM: %s' % (field, (path or '<contents>'))
+            raise GenericError('could not extract %s from POM: %s' % (field, (path or '<contents>')))
     return values
 
 def pom_to_maven_info(pominfo):
@@ -1358,7 +1358,7 @@ def genMockConfig(name, arch, managed=False, repoid=None, tag_name=None, **opts)
         urls = [opts['url']]
     else:
         if not (repoid and tag_name):
-            raise GenericError, "please provide a repo and tag"
+            raise GenericError("please provide a repo and tag")
         topurls = opts.get('topurls')
         if not topurls:
             #cli command still passes plain topurl
@@ -1544,7 +1544,7 @@ def openRemoteFile(relpath, topurl=None, topdir=None, tempdir=None):
         fn = "%s/%s" % (topdir, relpath)
         fo = open(fn)
     else:
-        raise GenericError, "No access method for remote file: %s" % relpath
+        raise GenericError("No access method for remote file: %s" % relpath)
     return fo
 
 
@@ -2072,7 +2072,7 @@ class ClientSession(object):
                 ccache.init(cprinc)
                 ccache.init_creds_keytab(principal=cprinc, keytab=keytab)
             else:
-                raise AuthError, 'cannot specify a principal without a keytab'
+                raise AuthError('cannot specify a principal without a keytab')
         else:
             # We're trying to log ourself in.  Connect using existing credentials.
             cprinc = ccache.principal()
@@ -2162,7 +2162,7 @@ class ClientSession(object):
         finally:
             self.opts = old_opts
         if not sinfo:
-            raise AuthError, 'unable to obtain a session'
+            raise AuthError('unable to obtain a session')
 
         self.setSession(sinfo)
 
@@ -2200,7 +2200,7 @@ class ClientSession(object):
         finally:
             self.opts = old_opts
         if not sinfo:
-            raise AuthError, 'unable to obtain a session'
+            raise AuthError('unable to obtain a session')
 
         self.opts['cert'] = cert
         self.opts['serverca'] = serverca
@@ -2427,7 +2427,7 @@ class ClientSession(object):
         method call, or a map containing "faultCode" and "faultString" keys, describing the
         error that occurred during the method call."""
         if not self.multicall:
-            raise GenericError, 'ClientSession.multicall must be set to True before calling multiCall()'
+            raise GenericError('ClientSession.multicall must be set to True before calling multiCall()')
         self.multicall = False
         if len(self._calls) == 0:
             return []
@@ -2446,7 +2446,7 @@ class ClientSession(object):
 
     def __getattr__(self, name):
         #if name[:1] == '_':
-        #    raise AttributeError, "no attribute %r" % name
+        #    raise AttributeError("no attribute %r" % name)
         return VirtualMethod(self._callMethod, name)
 
     def fastUpload(self, localfile, path, name=None, callback=None, blocksize=None, overwrite=False):
@@ -2454,7 +2454,7 @@ class ClientSession(object):
             blocksize = self.opts.get('upload_blocksize', 1048576)
 
         if not self.logged_in:
-            raise ActionNotAllowed, 'You must be logged in to upload files'
+            raise ActionNotAllowed('You must be logged in to upload files')
         if name is None:
             name = os.path.basename(localfile)
         self.logger.debug("Fast upload: %s to %s/%s", localfile, path, name)
@@ -2478,10 +2478,10 @@ class ClientSession(object):
             hexdigest = util.adler32_constructor(chunk).hexdigest()
             full_chksum.update(chunk)
             if result['size'] != len(chunk):
-                raise GenericError, "server returned wrong chunk size: %s != %s" % (result['size'], len(chunk))
+                raise GenericError("server returned wrong chunk size: %s != %s" % (result['size'], len(chunk)))
             if result['hexdigest'] != hexdigest:
-                raise GenericError, 'upload checksum failed: %s != %s' \
-                        % (result['hexdigest'], hexdigest)
+                raise GenericError('upload checksum failed: %s != %s' \
+                        % (result['hexdigest'], hexdigest))
             ofs += len(chunk)
             now = time.time()
             t1 = max(now - lap, 0.00001)
@@ -2496,17 +2496,17 @@ class ClientSession(object):
             chk_opts['verify'] = 'adler32'
         result = self._callMethod('checkUpload', (path, name), chk_opts)
         if int(result['size']) != ofs:
-            raise GenericError, "Uploaded file is wrong length: %s/%s, %s != %s" \
-                    % (path, name, result['size'], ofs)
+            raise GenericError("Uploaded file is wrong length: %s/%s, %s != %s" \
+                    % (path, name, result['size'], ofs))
         if problems and result['hexdigest'] != full_chksum.hexdigest():
-            raise GenericError, "Uploaded file has wrong checksum: %s/%s, %s != %s" \
-                    % (path, name, result['hexdigest'], full_chksum.hexdigest())
+            raise GenericError("Uploaded file has wrong checksum: %s/%s, %s != %s" \
+                    % (path, name, result['hexdigest'], full_chksum.hexdigest()))
         self.logger.debug("Fast upload: %s complete. %i bytes in %.1f seconds", localfile, size, t2)
 
     def _prepUpload(self, chunk, offset, path, name, verify="adler32", overwrite=False):
         """prep a rawUpload call"""
         if not self.logged_in:
-            raise ActionNotAllowed, "you must be logged in to upload"
+            raise ActionNotAllowed("you must be logged in to upload")
         args = self.sinfo.copy()
         args['callnum'] = self.callnum
         args['filename'] = name
@@ -2584,7 +2584,7 @@ class ClientSession(object):
                     tries += 1
                     continue
                 else:
-                    raise GenericError, "Error uploading file %s, offset %d" %(path, offset)
+                    raise GenericError("Error uploading file %s, offset %d" %(path, offset))
             if size == 0:
                 break
             ofs += size
@@ -2610,7 +2610,7 @@ class ClientSession(object):
         Note: This method does not work with multicall.
         """
         if self.multicall:
-            raise GenericError, 'downloadTaskOutput() may not be called during a multicall'
+            raise GenericError('downloadTaskOutput() may not be called during a multicall')
         result = self.callMethod('downloadTaskOutput', taskID, fileName, offset, size)
         return base64.decodestring(result)
 
diff --git a/koji/auth.py b/koji/auth.py
index b935b08..ae74d85 100644
--- a/koji/auth.py
+++ b/koji/auth.py
@@ -77,7 +77,7 @@ class Session(object):
             id = long(args['session-id'][0])
             key = args['session-key'][0]
         except KeyError, field:
-            raise koji.AuthError, '%s not specified in session args' % field
+            raise koji.AuthError('%s not specified in session args' % field)
         try:
             callnum = args['callnum'][0]
         except:
@@ -107,22 +107,22 @@ class Session(object):
         c.execute(q, locals())
         row = c.fetchone()
         if not row:
-            raise koji.AuthError, 'Invalid session or bad credentials'
+            raise koji.AuthError('Invalid session or bad credentials')
         session_data = dict(zip(aliases, row))
         #check for expiration
         if session_data['expired']:
-            raise koji.AuthExpired, 'session "%i" has expired' % id
+            raise koji.AuthExpired('session "%i" has expired' % id)
         #check for callnum sanity
         if callnum is not None:
             try:
                 callnum = int(callnum)
             except (ValueError, TypeError):
-                raise koji.AuthError, "Invalid callnum: %r" % callnum
+                raise koji.AuthError("Invalid callnum: %r" % callnum)
             lastcall = session_data['callnum']
             if lastcall is not None:
                 if lastcall > callnum:
-                    raise koji.SequenceError, "%d > %d (session %d)" \
-                            % (lastcall, callnum, id)
+                    raise koji.SequenceError("%d > %d (session %d)" \
+                            % (lastcall, callnum, id))
                 elif lastcall == callnum:
                     #Some explanation:
                     #This function is one of the few that performs its own commit.
@@ -134,9 +134,9 @@ class Session(object):
                     #return. Data was changed, so we cannot simply try the call again.
                     method = getattr(context, 'method', 'UNKNOWN')
                     if method not in RetryWhitelist:
-                        raise koji.RetryError, \
+                        raise koji.RetryError(
                             "unable to retry call %d (method %s) for session %d" \
-                            % (callnum, method, id)
+                            % (callnum, method, id))
 
         # read user data
         #historical note:
@@ -149,7 +149,7 @@ class Session(object):
         user_data = dict(zip(fields, c.fetchone()))
 
         if user_data['status'] != koji.USER_STATUS['NORMAL']:
-            raise koji.AuthError, 'logins by %s are not allowed' % user_data['name']
+            raise koji.AuthError('logins by %s are not allowed' % user_data['name'])
         #check for exclusive sessions
         if session_data['exclusive']:
             #we are the exclusive session for this user
@@ -218,7 +218,7 @@ class Session(object):
                 self._host_id = self._getHostId()
             return self._host_id
         else:
-            raise AttributeError, "%s" % name
+            raise AttributeError("%s" % name)
 
     def __str__(self):
         # convenient display for debugging
@@ -232,7 +232,7 @@ class Session(object):
 
     def validate(self):
         if self.lockerror:
-            raise koji.AuthLockError, self.lockerror
+            raise koji.AuthLockError(self.lockerror)
         return True
 
     def get_remote_ip(self, override=None):
@@ -254,20 +254,20 @@ class Session(object):
         cursor.execute(query, locals())
         result = cursor.fetchone()
         if not result:
-            raise koji.AuthError, 'invalid user_id: %s' % user_id
+            raise koji.AuthError('invalid user_id: %s' % user_id)
         name, usertype, status = result
 
         if status != koji.USER_STATUS['NORMAL']:
-            raise koji.AuthError, 'logins by %s are not allowed' % name
+            raise koji.AuthError('logins by %s are not allowed' % name)
 
     def login(self, user, password, opts=None):
         """create a login session"""
         if opts is None:
             opts = {}
         if not isinstance(password, str) or len(password) == 0:
-            raise koji.AuthError, 'invalid username or password'
+            raise koji.AuthError('invalid username or password')
         if self.logged_in:
-            raise koji.GenericError, "Already logged in"
+            raise koji.GenericError("Already logged in")
         hostip = self.get_remote_ip(override=opts.get('hostip'))
 
         # check passwd
@@ -277,7 +277,7 @@ class Session(object):
         c.execute(q, locals())
         r = c.fetchone()
         if not r:
-            raise koji.AuthError, 'invalid username or password'
+            raise koji.AuthError('invalid username or password')
         user_id = r[0]
 
         self.checkLoginAllowed(user_id)
@@ -295,10 +295,10 @@ class Session(object):
         Kerberos principal.  The principal must be an authorized
         "proxy_principal" in the server config."""
         if self.logged_in:
-            raise koji.AuthError, "Already logged in"
+            raise koji.AuthError("Already logged in")
 
         if not (context.opts.get('AuthPrincipal') and context.opts.get('AuthKeytab')):
-            raise koji.AuthError, 'not configured for Kerberos authentication'
+            raise koji.AuthError('not configured for Kerberos authentication')
 
         ctx = krbV.default_context()
         srvprinc = krbV.Principal(name=context.opts.get('AuthPrincipal'), context=ctx)
@@ -322,8 +322,8 @@ class Session(object):
             if cprinc.name in proxyprincs:
                 login_principal = proxyuser
             else:
-                raise koji.AuthError, \
-                      'Kerberos principal %s is not authorized to log in other users' % cprinc.name
+                raise koji.AuthError(
+                      'Kerberos principal %s is not authorized to log in other users' % cprinc.name)
         else:
             login_principal = cprinc.name
         user_id = self.getUserIdFromKerberos(login_principal)
@@ -331,7 +331,7 @@ class Session(object):
             if context.opts.get('LoginCreatesUser'):
                 user_id = self.createUserFromKerberos(login_principal)
             else:
-                raise koji.AuthError, 'Unknown Kerberos principal: %s' % login_principal
+                raise koji.AuthError('Unknown Kerberos principal: %s' % login_principal)
 
         self.checkLoginAllowed(user_id)
 
@@ -372,7 +372,7 @@ class Session(object):
 
     def sslLogin(self, proxyuser=None):
         if self.logged_in:
-            raise koji.AuthError, "Already logged in"
+            raise koji.AuthError("Already logged in")
 
         if context.environ.get('REMOTE_USER'):
             username = context.environ.get('REMOTE_USER')
@@ -380,12 +380,12 @@ class Session(object):
             authtype = koji.AUTHTYPE_GSSAPI
         else:
             if context.environ.get('SSL_CLIENT_VERIFY') != 'SUCCESS':
-                raise koji.AuthError, 'could not verify client: %s' % context.environ.get('SSL_CLIENT_VERIFY')
+                raise koji.AuthError('could not verify client: %s' % context.environ.get('SSL_CLIENT_VERIFY'))
 
             name_dn_component = context.opts.get('DNUsernameComponent', 'CN')
             username = context.environ.get('SSL_CLIENT_S_DN_%s' % name_dn_component)
             if not username:
-                raise koji.AuthError, 'unable to get user information (%s) from client certificate' % name_dn_component
+                raise koji.AuthError('unable to get user information (%s) from client certificate' % name_dn_component)
             client_dn = context.environ.get('SSL_CLIENT_S_DN')
             authtype = koji.AUTHTYPE_SSL
 
@@ -395,7 +395,7 @@ class Session(object):
                 # the SSL-authenticated user authorized to login other users
                 username = proxyuser
             else:
-                raise koji.AuthError, '%s is not authorized to login other users' % client_dn
+                raise koji.AuthError('%s is not authorized to login other users' % client_dn)
 
         cursor = context.cnx.cursor()
         query = """SELECT id FROM users
@@ -408,7 +408,7 @@ class Session(object):
             if context.opts.get('LoginCreatesUser'):
                 user_id = self.createUser(username)
             else:
-                raise koji.AuthError, 'Unknown user: %s' % username
+                raise koji.AuthError('Unknown user: %s' % username)
 
         self.checkLoginAllowed(user_id)
 
@@ -421,10 +421,10 @@ class Session(object):
         """Make this session exclusive"""
         c = context.cnx.cursor()
         if self.master is not None:
-            raise koji.GenericError, "subsessions cannot become exclusive"
+            raise koji.GenericError("subsessions cannot become exclusive")
         if self.exclusive:
             #shouldn't happen
-            raise koji.GenericError, "session is already exclusive"
+            raise koji.GenericError("session is already exclusive")
         user_id = self.user_id
         session_id = self.id
         #acquire a row lock on the user entry
@@ -443,7 +443,7 @@ class Session(object):
                 q = """UPDATE sessions SET expired=TRUE,"exclusive"=NULL WHERE id=%(excl_id)s"""
                 c.execute(q, locals())
             else:
-                raise koji.AuthLockError, "Cannot get exclusive session"
+                raise koji.AuthLockError("Cannot get exclusive session")
         #mark this session exclusive
         q = """UPDATE sessions SET "exclusive"=TRUE WHERE id=%(session_id)s"""
         c.execute(q, locals())
@@ -461,7 +461,7 @@ class Session(object):
         """expire a login session"""
         if not self.logged_in:
             #XXX raise an error?
-            raise koji.AuthError, "Not logged in"
+            raise koji.AuthError("Not logged in")
         update = """UPDATE sessions
         SET expired=TRUE,exclusive=NULL
         WHERE id = %(id)i OR master = %(id)i"""
@@ -475,7 +475,7 @@ class Session(object):
         """expire a subsession"""
         if not self.logged_in:
             #XXX raise an error?
-            raise koji.AuthError, "Not logged in"
+            raise koji.AuthError("Not logged in")
         update = """UPDATE sessions
         SET expired=TRUE,exclusive=NULL
         WHERE id = %(session_id)i AND master = %(master)i"""
@@ -517,7 +517,7 @@ class Session(object):
     def subsession(self):
         "Create a subsession"
         if not self.logged_in:
-            raise koji.AuthError, "Not logged in"
+            raise koji.AuthError("Not logged in")
         master = self.master
         if master is None:
             master = self.id
@@ -536,11 +536,11 @@ class Session(object):
 
     def assertPerm(self, name):
         if not self.hasPerm(name) and not self.hasPerm('admin'):
-            raise koji.ActionNotAllowed, "%s permission required" % name
+            raise koji.ActionNotAllowed("%s permission required" % name)
 
     def assertLogin(self):
         if not self.logged_in:
-            raise koji.ActionNotAllowed, "you must be logged in for this operation"
+            raise koji.ActionNotAllowed("you must be logged in for this operation")
 
     def hasGroup(self, group_id):
         if not self.logged_in:
@@ -555,7 +555,7 @@ class Session(object):
 
     def assertUser(self, user_id):
         if not self.isUser(user_id) and not self.hasPerm('admin'):
-            raise koji.ActionNotAllowed, "not owner"
+            raise koji.ActionNotAllowed("not owner")
 
     def _getHostId(self):
         '''Using session data, find host id (if there is one)'''
@@ -594,17 +594,17 @@ class Session(object):
         Return the user_id of the newly-created user.
         """
         if not name:
-            raise koji.GenericError, 'a user must have a non-empty name'
+            raise koji.GenericError('a user must have a non-empty name')
 
         if usertype == None:
             usertype = koji.USERTYPES['NORMAL']
         elif not koji.USERTYPES.get(usertype):
-            raise koji.GenericError, 'invalid user type: %s' % usertype
+            raise koji.GenericError('invalid user type: %s' % usertype)
 
         if status == None:
             status = koji.USER_STATUS['NORMAL']
         elif not koji.USER_STATUS.get(status):
-            raise koji.GenericError, 'invalid status: %s' % status
+            raise koji.GenericError('invalid status: %s' % status)
 
         cursor = context.cnx.cursor()
         select = """SELECT nextval('users_id_seq')"""
@@ -627,7 +627,7 @@ class Session(object):
         r = cursor.fetchall()
         if len(r) != 1:
             context.cnx.rollback()
-            raise koji.AuthError, 'could not automatically associate Kerberos Principal with existing user %s' % (name,)
+            raise koji.AuthError('could not automatically associate Kerberos Principal with existing user %s' % name)
         else:
             context.cnx.commit()
             return r[0][0]
@@ -638,7 +638,7 @@ class Session(object):
         Return the ID of the newly created user."""
         atidx = krb_principal.find('@')
         if atidx == -1:
-            raise koji.AuthError, 'invalid Kerberos principal: %s' % krb_principal
+            raise koji.AuthError('invalid Kerberos principal: %s' % krb_principal)
         user_name = krb_principal[:atidx]
 
         # check if user already exists
@@ -652,7 +652,7 @@ class Session(object):
         else:
             existing_user_krb = r[0]
             if existing_user_krb is not None:
-                raise koji.AuthError, 'user %s already associated with other Kerberos principal: %s' % (user_name, existing_user_krb)
+                raise koji.AuthError('user %s already associated with other Kerberos principal: %s' % (user_name, existing_user_krb))
             return self.setKrbPrincipal(user_name, krb_principal)
 
 def get_user_groups(user_id):
diff --git a/koji/compatrequests.py b/koji/compatrequests.py
index 7f6a14d..5d77bf3 100644
--- a/koji/compatrequests.py
+++ b/koji/compatrequests.py
@@ -90,7 +90,7 @@ class Session(object):
             cnxOpts = {}
             cnxClass = httplib.HTTPConnection
         else:
-            raise IOError, "unsupported protocol: %s" % scheme
+            raise IOError("unsupported protocol: %s" % scheme)
 
         timeout_compat = False
         if timeout:
diff --git a/koji/daemon.py b/koji/daemon.py
index 4882a1c..db0ad01 100644
--- a/koji/daemon.py
+++ b/koji/daemon.py
@@ -207,7 +207,7 @@ class SCM(object):
         self.logger = logging.getLogger('koji.build.SCM')
 
         if not SCM.is_scm_url(url):
-            raise koji.GenericError, 'Invalid SCM URL: %s' % url
+            raise koji.GenericError('Invalid SCM URL: %s' % url)
 
         self.url = url
         scheme, user, host, path, query, fragment = self._parse_url()
@@ -227,7 +227,7 @@ class SCM(object):
                 break
         else:
             # should never happen
-            raise koji.GenericError, 'Invalid SCM URL: %s' % url
+            raise koji.GenericError('Invalid SCM URL: %s' % url)
 
     def _parse_url(self):
         """
@@ -253,10 +253,10 @@ class SCM(object):
                 # Don't return an empty string
                 user = None
             elif ':' in user:
-                raise koji.GenericError, 'username:password format not supported: %s' % user
+                raise koji.GenericError('username:password format not supported: %s' % user)
             netloc = userhost[1]
         elif len(userhost) > 2:
-            raise koji.GenericError, 'Invalid username@hostname specified: %s' % netloc
+            raise koji.GenericError('Invalid username@hostname specified: %s' % netloc)
 
         # ensure that path and query do not end in /
         if path.endswith('/'):
@@ -266,15 +266,15 @@ class SCM(object):
 
         # check for validity: params should be empty, query may be empty, everything else should be populated
         if params:
-            raise koji.GenericError, 'Unable to parse SCM URL: %s . Params element %s should be empty.' % (self.url, params)
+            raise koji.GenericError('Unable to parse SCM URL: %s . Params element %s should be empty.' % (self.url, params))
         if not scheme:
-            raise koji.GenericError, 'Unable to parse SCM URL: %s . Could not find the scheme element.' % self.url
+            raise koji.GenericError('Unable to parse SCM URL: %s . Could not find the scheme element.' % self.url)
         if not netloc:
-            raise koji.GenericError, 'Unable to parse SCM URL: %s . Could not find the netloc element.' % self.url
+            raise koji.GenericError('Unable to parse SCM URL: %s . Could not find the netloc element.' % self.url)
         if not path:
-            raise koji.GenericError, 'Unable to parse SCM URL: %s . Could not find the path element.' % self.url
+            raise koji.GenericError('Unable to parse SCM URL: %s . Could not find the path element.' % self.url)
         if not fragment:
-            raise koji.GenericError, 'Unable to parse SCM URL: %s . Could not find the fragment element.' % self.url
+            raise koji.GenericError('Unable to parse SCM URL: %s . Could not find the fragment element.' % self.url)
 
         # return parsed values
         return (scheme, user, netloc, path, query, fragment)
@@ -315,7 +315,7 @@ class SCM(object):
             else:
                 self.logger.warn('Ignoring incorrectly formatted SCM host:repository: %s' % allowed_scm)
         else:
-            raise koji.BuildError, '%s:%s is not in the list of allowed SCMs' % (self.host, self.repository)
+            raise koji.BuildError('%s:%s is not in the list of allowed SCMs' % (self.host, self.repository))
 
     def checkout(self, scmdir, session=None, uploadpath=None, logfile=None):
         """
@@ -344,8 +344,8 @@ class SCM(object):
                 _count[0] += 1
                 if log_output(session, cmd[0], cmd, logfile, uploadpath,
                               cwd=chdir, logerror=1, append=append, env=env):
-                    raise koji.BuildError, 'Error running %s command "%s", see %s for details' % \
-                        (self.scmtype, ' '.join(cmd), os.path.basename(logfile))
+                    raise koji.BuildError('Error running %s command "%s", see %s for details' % \
+                        (self.scmtype, ' '.join(cmd), os.path.basename(logfile)))
 
         if self.scmtype == 'CVS':
             pserver = ':pserver:%s@%s:%s' % ((self.user or 'anonymous'), self.host, self.repository)
@@ -354,7 +354,7 @@ class SCM(object):
 
         elif self.scmtype == 'CVS+SSH':
             if not self.user:
-                raise koji.BuildError, 'No user specified for repository access scheme: %s' % self.scheme
+                raise koji.BuildError('No user specified for repository access scheme: %s' % self.scheme)
 
             cvsserver = ':ext:%s@%s:%s' % (self.user, self.host, self.repository)
             module_checkout_cmd = ['cvs', '-d', cvsserver, 'checkout', '-r', self.revision, self.module]
@@ -392,7 +392,7 @@ class SCM(object):
 
         elif self.scmtype == 'GIT+SSH':
             if not self.user:
-                raise koji.BuildError, 'No user specified for repository access scheme: %s' % self.scheme
+                raise koji.BuildError('No user specified for repository access scheme: %s' % self.scheme)
             gitrepo = 'git+ssh://%s@%s%s' % (self.user, self.host, self.repository)
             commonrepo = os.path.dirname(gitrepo) + '/common'
             checkout_path = os.path.basename(self.repository)
@@ -429,14 +429,14 @@ class SCM(object):
 
         elif self.scmtype == 'SVN+SSH':
             if not self.user:
-                raise koji.BuildError, 'No user specified for repository access scheme: %s' % self.scheme
+                raise koji.BuildError('No user specified for repository access scheme: %s' % self.scheme)
 
             svnserver = 'svn+ssh://%s@%s%s' % (self.user, self.host, self.repository)
             module_checkout_cmd = ['svn', 'checkout', '-r', self.revision, '%s/%s' % (svnserver, self.module), self.module]
             common_checkout_cmd = ['svn', 'checkout', '%s/common' % svnserver]
 
         else:
-            raise koji.BuildError, 'Unknown SCM type: %s' % self.scmtype
+            raise koji.BuildError('Unknown SCM type: %s' % self.scmtype)
 
         # perform checkouts
         _run(module_checkout_cmd, chdir=scmdir, fatal=True)
@@ -719,7 +719,7 @@ class TaskManager(object):
                 #  - task is forcibly reassigned/unassigned
                 tinfo = self.session.getTaskInfo(id)
                 if tinfo is None:
-                    raise koji.GenericError, "Invalid task %r (pid %r)" % (id, pid)
+                    raise koji.GenericError("Invalid task %r (pid %r)" % (id, pid))
                 elif tinfo['state'] == koji.TASK_STATES['CANCELED']:
                     self.logger.info("Killing canceled task %r (pid %r)" % (id, pid))
                     if self.cleanupTask(id):
@@ -808,7 +808,7 @@ class TaskManager(object):
                     return True
             else:
                 #should not happen
-                raise Exception, "Invalid task state reported by server"
+                raise Exception("Invalid task state reported by server")
         return False
 
     def checkRelAvail(self, bin_avail, avail):
@@ -829,7 +829,7 @@ class TaskManager(object):
         if pid is None:
             pid = self.pids.get(task_id)
             if not pid:
-                raise koji.GenericError, "No pid for task %i" % task_id
+                raise koji.GenericError("No pid for task %i" % task_id)
         prefix = "Task %i (pid %i)" % (task_id, pid)
         try:
             (childpid, status) = os.waitpid(pid, os.WNOHANG)
@@ -974,7 +974,7 @@ class TaskManager(object):
         """
         pid = self.pids.get(task_id)
         if not pid:
-            raise koji.GenericError, "No pid for task %i" % task_id
+            raise koji.GenericError("No pid for task %i" % task_id)
         children = self._childPIDs(pid)
         if children:
             # send SIGINT once to let mock mock try to clean up
@@ -1006,7 +1006,7 @@ class TaskManager(object):
         br_path = self.options.mockdir
         if not os.path.exists(br_path):
             self.logger.error("No such directory: %s" % br_path)
-            raise IOError, "No such directory: %s" % br_path
+            raise IOError("No such directory: %s" % br_path)
         fs_stat = os.statvfs(br_path)
         available = fs_stat.f_bavail * fs_stat.f_bsize
         availableMB = available / 1024 / 1024
@@ -1079,7 +1079,7 @@ class TaskManager(object):
         if self.handlers.has_key(method):
             handlerClass = self.handlers[method]
         else:
-            raise koji.GenericError, "No handler found for method '%s'" % method
+            raise koji.GenericError("No handler found for method '%s'" % method)
         task_info = self.session.getTaskInfo(task['id'], request=True)
         if task_info.get('request') is None:
             self.logger.warn("Task '%s' has no request" % task['id'])
diff --git a/koji/db.py b/koji/db.py
index 8c0bba6..f7fe8e1 100644
--- a/koji/db.py
+++ b/koji/db.py
@@ -57,19 +57,19 @@ class DBWrapper:
 
     def __getattr__(self, key):
         if not self.cnx:
-            raise StandardError, 'connection is closed'
+            raise StandardError('connection is closed')
         return getattr(self.cnx, key)
 
     def cursor(self, *args, **kw):
         if not self.cnx:
-            raise StandardError, 'connection is closed'
+            raise StandardError('connection is closed')
         return CursorWrapper(self.cnx.cursor(*args, **kw))
 
     def close(self):
         # Rollback any uncommitted changes and clear the connection so
         # this DBWrapper is no longer usable after close()
         if not self.cnx:
-            raise StandardError, 'connection is closed'
+            raise StandardError('connection is closed')
         self.cnx.cursor().execute('ROLLBACK')
         #We do this rather than cnx.rollback to avoid opening a new transaction
         #If our connection gets recycled cnx.rollback will be called then.
diff --git a/koji/plugin.py b/koji/plugin.py
index 64e18ad..fe116b0 100644
--- a/koji/plugin.py
+++ b/koji/plugin.py
@@ -68,11 +68,11 @@ class PluginTracker(object):
             #(no '.' -- it causes problems)
             mod_name = self.prefix + name
         if sys.modules.has_key(mod_name) and not reload:
-            raise koji.PluginError, 'module name conflict: %s' % mod_name
+            raise koji.PluginError('module name conflict: %s' % mod_name)
         if path is None:
             path = self.searchpath
         if path is None:
-            raise koji.PluginError, "empty module search path"
+            raise koji.PluginError("empty module search path")
         file, pathname, description = imp.find_module(name, self.pathlist(path))
         try:
             plugin = imp.load_module(mod_name, file, pathname, description)
@@ -151,14 +151,14 @@ def ignore_error(f):
 
 def register_callback(cbtype, func):
     if not cbtype in callbacks:
-        raise koji.PluginError, '"%s" is not a valid callback type' % cbtype
+        raise koji.PluginError('"%s" is not a valid callback type' % cbtype)
     if not callable(func):
-        raise koji.PluginError, '%s is not callable' % getattr(func, '__name__', 'function')
+        raise koji.PluginError('%s is not callable' % getattr(func, '__name__', 'function'))
     callbacks[cbtype].append(func)
 
 def run_callbacks(cbtype, *args, **kws):
     if not cbtype in callbacks:
-        raise koji.PluginError, '"%s" is not a valid callback type' % cbtype
+        raise koji.PluginError('"%s" is not a valid callback type' % cbtype)
     for func in callbacks[cbtype]:
         try:
             func(cbtype, *args, **kws)
@@ -168,4 +168,4 @@ def run_callbacks(cbtype, *args, **kws):
                 logging.getLogger('koji.plugin').warn(msg, exc_info=True)
             else:
                 tb = ''.join(traceback.format_exception(*sys.exc_info()))
-                raise koji.CallbackError, '%s:\n%s' % (msg, tb)
+                raise koji.CallbackError('%s:\n%s' % (msg, tb))
diff --git a/koji/policy.py b/koji/policy.py
index 097656f..4e1bd61 100644
--- a/koji/policy.py
+++ b/koji/policy.py
@@ -73,7 +73,7 @@ class HasTest(BaseSimpleTest):
         try:
             self.field = str.split()[1]
         except IndexError:
-            raise koji.GenericError, "Invalid or missing field in policy test"
+            raise koji.GenericError("Invalid or missing field in policy test")
 
     def run(self, data):
         return self.field in data
@@ -161,7 +161,7 @@ class CompareTest(BaseSimpleTest):
             cmp, value = str.split(None, 2)[1:]
         self.func = self.operators.get(cmp, None)
         if self.func is None:
-            raise koji.GenericError, "Invalid comparison in test."
+            raise koji.GenericError("Invalid comparison in test.")
         try:
             self.value = int(value)
         except ValueError:
@@ -218,13 +218,13 @@ class SimpleRuleSet(object):
                 cursor = child
             elif action == '}':
                 if not stack:
-                    raise koji.GenericError, "nesting error in rule set"
+                    raise koji.GenericError("nesting error in rule set")
                 cursor = stack.pop()
             else:
                 cursor.append(rule)
         if stack:
             # unclosed {
-            raise koji.GenericError, "nesting error in rule set"
+            raise koji.GenericError("nesting error in rule set")
 
     def parse_line(self, line):
         """Parse line as a rule
@@ -259,7 +259,7 @@ class SimpleRuleSet(object):
         if pos == -1:
             pos = line.rfind('!!')
             if pos == -1:
-                raise Exception, "bad policy line: %s" % line
+                raise Exception("bad policy line: %s" % line)
             negate = True
         tests = line[:pos]
         action = line[pos+2:]
@@ -273,7 +273,7 @@ class SimpleRuleSet(object):
         try:
             return self.tests[name](str)
         except KeyError:
-            raise koji.GenericError, "missing test handler: %s" % name
+            raise koji.GenericError("missing test handler: %s" % name)
 
     def all_actions(self):
         """report a list of all actions in the ruleset
diff --git a/koji/server.py b/koji/server.py
index 60b29a9..9229bb6 100644
--- a/koji/server.py
+++ b/koji/server.py
@@ -118,12 +118,12 @@ class WSGIWrapper(object):
 
         we don't use the write() callable in koji, so it will raise an error if called
         """
-        raise RuntimeError, "wsgi write() callable not supported"
+        raise RuntimeError("wsgi write() callable not supported")
 
     def start_response(self, status, headers, exc_info=None):
         #XXX we don't deal with exc_info
         if self.set_headers:
-            raise RuntimeError, "start_response() already called"
+            raise RuntimeError("start_response() already called")
         self.req.status = int(status[:3])
         for key, val in headers:
             if key.lower() == 'content-length':
@@ -143,7 +143,7 @@ class WSGIWrapper(object):
         except:
             sys.stderr.write(''.join(traceback.format_exception(*sys.exc_info())))
             sys.stderr.flush()
-            raise apache.SERVER_RETURN, apache.HTTP_INTERNAL_SERVER_ERROR
+            raise apache.SERVER_RETURN(apache.HTTP_INTERNAL_SERVER_ERROR)
 
     def write_result(self, result):
         """called by run() to handle the application's result value"""
@@ -156,7 +156,7 @@ class WSGIWrapper(object):
             #slower version -- need to check for set_headers
             for chunk in result:
                 if chunk and not self.set_headers:
-                    raise RuntimeError, "write() called before start_response()"
+                    raise RuntimeError("write() called before start_response()")
                 write(chunk)
         if not req.bytes_sent:
             #application sent nothing back
diff --git a/koji/ssl/SSLCommon.py b/koji/ssl/SSLCommon.py
index 8b6d988..199a858 100644
--- a/koji/ssl/SSLCommon.py
+++ b/koji/ssl/SSLCommon.py
@@ -33,7 +33,7 @@ def CreateSSLContext(certs):
     peer_ca_cert = certs['peer_ca_cert']
     for f in key_and_cert, peer_ca_cert:
         if f and not os.access(f, os.R_OK):
-            raise StandardError, "%s does not exist or is not readable" % f
+            raise StandardError("%s does not exist or is not readable" % f)
 
     ctx = SSL.Context(SSL.SSLv23_METHOD)   # Use best possible TLS Method
     ctx.use_certificate_file(key_and_cert)
@@ -77,4 +77,4 @@ class PlgHTTPSConnection(httplib.HTTPConnection):
                 continue
             break
         else:
-            raise socket.error, "failed to connect"
+            raise socket.error("failed to connect")
diff --git a/koji/tasks.py b/koji/tasks.py
index 531591c..0e27ab0 100644
--- a/koji/tasks.py
+++ b/koji/tasks.py
@@ -55,11 +55,11 @@ def umount_all(topdir):
         cmd = ['umount', '-l', path]
         rv = os.spawnvp(os.P_WAIT, cmd[0], cmd)
         if rv != 0:
-            raise koji.GenericError, 'umount failed (exit code %r) for %s' % (rv, path)
+            raise koji.GenericError('umount failed (exit code %r) for %s' % (rv, path))
     #check mounts again
     remain = scan_mounts(topdir)
     if remain:
-        raise koji.GenericError, "Unmounting incomplete: %r" % remain
+        raise koji.GenericError("Unmounting incomplete: %r" % remain)
 
 def safe_rmtree(path, unmount=False, strict=True):
     logger = logging.getLogger("koji.build")
@@ -87,7 +87,7 @@ def safe_rmtree(path, unmount=False, strict=True):
     if rv != 0:
         logger.warn(msg)
         if strict:
-            raise koji.GenericError, msg
+            raise koji.GenericError(msg)
         else:
             return rv
     #them rmdir directories
@@ -98,7 +98,7 @@ def safe_rmtree(path, unmount=False, strict=True):
     if rv != 0:
         logger.warn(msg)
         if strict:
-            raise koji.GenericError, msg
+            raise koji.GenericError(msg)
     return rv
 
 class ServerExit(Exception):
@@ -125,7 +125,7 @@ class BaseTaskHandler(object):
     def __init__(self, id, method, params, session, options, workdir=None):
         self.id = id   #task id
         if method not in self.Methods:
-            raise koji.GenericError, 'method "%s" is not supported' % method
+            raise koji.GenericError('method "%s" is not supported' % method)
         self.method = method
         # handle named parameters
         self.params, self.opts = koji.decode_args(*params)
@@ -336,10 +336,10 @@ class BaseTaskHandler(object):
         #  c) is canonical
         host_arches = host['arches']
         if not host_arches:
-            raise koji.BuildError, "No arch list for this host: %s" % host['name']
+            raise koji.BuildError("No arch list for this host: %s" % host['name'])
         tag_arches = tag['arches']
         if not tag_arches:
-            raise koji.BuildError, "No arch list for tag: %s" % tag['name']
+            raise koji.BuildError("No arch list for tag: %s" % tag['name'])
         # index canonical host arches
         host_arches = set([koji.canonArch(a) for a in host_arches.split()])
         # index canonical tag arches
@@ -356,8 +356,8 @@ class BaseTaskHandler(object):
             return arch
         else:
             # no overlap
-            raise koji.BuildError, "host %s (%s) does not support any arches of tag %s (%s)" % \
-                (host['name'], ', '.join(host_arches), tag['name'], ', '.join(tag_arches))
+            raise koji.BuildError("host %s (%s) does not support any arches of tag %s (%s)" % \
+                (host['name'], ', '.join(host_arches), tag['name'], ', '.join(tag_arches)))
 
     def getRepo(self, tag):
         """
@@ -370,7 +370,7 @@ class BaseTaskHandler(object):
             taginfo = self.session.getTag(tag, strict=True)
             targets = self.session.getBuildTargets(buildTagID=taginfo['id'])
             if not targets:
-                raise koji.BuildError, 'no repo (and no target) for tag %s' % taginfo['name']
+                raise koji.BuildError('no repo (and no target) for tag %s' % taginfo['name'])
             #wait for it
             task_id = self.session.host.subtask(method='waitrepo',
                                                 arglist=[tag, None, None],
@@ -445,7 +445,7 @@ class DefaultTask(BaseTaskHandler):
     Methods = ['default']
     _taskWeight = 0.1
     def handler(self, *args, **opts):
-        raise koji.GenericError, "Invalid method: %s" % self.method
+        raise koji.GenericError("Invalid method: %s" % self.method)
 
 
 class ShutdownTask(BaseTaskHandler):
@@ -466,7 +466,7 @@ class RestartTask(BaseTaskHandler):
     def handler(self, host):
         #note: this is a foreground task
         if host['id'] != self.session.host.getID():
-            raise koji.GenericError, "Host mismatch"
+            raise koji.GenericError("Host mismatch")
         self.manager.restart_pending = True
         return "graceful restart initiated"
 
@@ -482,12 +482,12 @@ class RestartVerifyTask(BaseTaskHandler):
         tinfo = self.session.getTaskInfo(task_id)
         state = koji.TASK_STATES[tinfo['state']]
         if state != 'CLOSED':
-            raise koji.GenericError, "Stage one restart task is %s" % state
+            raise koji.GenericError("Stage one restart task is %s" % state)
         if host['id'] != self.session.host.getID():
-            raise koji.GenericError, "Host mismatch"
+            raise koji.GenericError("Host mismatch")
         if self.manager.start_time < tinfo['completion_ts']:
             start_time = time.asctime(time.localtime(self.manager.start_time))
-            raise koji.GenericError, "Restart failed - start time is %s" % start_time
+            raise koji.GenericError("Restart failed - start time is %s" % start_time)
 
 
 class RestartHostsTask(BaseTaskHandler):
@@ -498,7 +498,7 @@ class RestartHostsTask(BaseTaskHandler):
     def handler(self):
         hosts = self.session.listHosts(enabled=True)
         if not hosts:
-            raise koji.GenericError, "No hosts enabled"
+            raise koji.GenericError("No hosts enabled")
         this_host = self.session.host.getID()
         subtasks = []
         my_tasks = None
@@ -511,7 +511,7 @@ class RestartHostsTask(BaseTaskHandler):
             if host['id'] == this_host:
                 my_tasks = [task1, task2]
         if not my_tasks:
-            raise koji.GenericError, 'This host is not enabled'
+            raise koji.GenericError('This host is not enabled')
         self.wait(my_tasks[0])
         #see if we've restarted
         if not self.session.taskFinished(my_tasks[1]):
@@ -542,7 +542,7 @@ class DependantTask(BaseTaskHandler):
                 if self.session.taskFinished(task):
                     info = self.session.getTaskInfo(task)
                     if info and koji.TASK_STATES[info['state']] in ['CANCELED', 'FAILED']:
-                        raise koji.GenericError, "Dependency %s failed to complete." % info['id']
+                        raise koji.GenericError("Dependency %s failed to complete." % info['id'])
                     wait_list.remove(task)
             # let the system rest before polling again
             time.sleep(1)
diff --git a/koji/util.py b/koji/util.py
index 50d203d..1eb32e8 100644
--- a/koji/util.py
+++ b/koji/util.py
@@ -159,7 +159,7 @@ def call_with_argcheck(func, args, kwargs=None):
             # The stack is only one high, so the error occurred in this function.
             # Therefore, we assume the TypeError is due to a parameter mismatch
             # in the above function call.
-            raise koji.ParameterError, str(e)
+            raise koji.ParameterError(str(e))
         raise
 
 
@@ -275,7 +275,7 @@ class LazyRecord(object):
 
 def lazysetattr(object, name, func, args, kwargs=None, cache=False):
     if not isinstance(object, LazyRecord):
-        raise TypeError, 'object does not support lazy attributes'
+        raise TypeError('object does not support lazy attributes')
     value = LazyValue(func, args, kwargs=kwargs, cache=cache)
     setattr(object, name, value)
 
@@ -503,7 +503,7 @@ def tsort(parts):
         parts = dict([(name, deps - level) for name, deps in parts.iteritems()
                       if name not in level])
     if parts:
-        raise ValueError, 'total ordering not possible'
+        raise ValueError('total ordering not possible')
     return result
 
 class MavenConfigOptAdapter(object):
@@ -527,7 +527,7 @@ class MavenConfigOptAdapter(object):
             elif name in self.MULTILINE:
                 value = value.splitlines()
             return value
-        raise AttributeError, name
+        raise AttributeError(name)
 
 def maven_opts(values, chain=False, scratch=False):
     """
@@ -552,7 +552,7 @@ def maven_opts(values, chain=False, scratch=False):
     for env in getattr(values, 'envs', []):
         fields = env.split('=', 1)
         if len(fields) != 2:
-            raise ValueError, "Environment variables must be in NAME=VALUE format"
+            raise ValueError("Environment variables must be in NAME=VALUE format")
         envs[fields[0]] = fields[1]
     if envs:
         opts['envs'] = envs
@@ -601,14 +601,14 @@ def parse_maven_params(confs, chain=False, scratch=False):
         elif buildtype == 'wrapper':
             params = wrapper_params(config, package, chain=chain, scratch=scratch)
             if len(params.get('buildrequires')) != 1:
-                raise ValueError, "A wrapper-rpm must depend on exactly one package"
+                raise ValueError("A wrapper-rpm must depend on exactly one package")
         else:
-            raise ValueError, "Unsupported build type: %s" % buildtype
+            raise ValueError("Unsupported build type: %s" % buildtype)
         if not 'scmurl' in params:
-            raise ValueError, "%s is missing the scmurl parameter" % package
+            raise ValueError("%s is missing the scmurl parameter" % package)
         builds[package] = params
     if not builds:
-        raise ValueError, "No sections found in: %s" % ', '.join(confs)
+        raise ValueError("No sections found in: %s" % ', '.join(confs))
     return builds
 
 def parse_maven_param(confs, chain=False, scratch=False, section=None):
@@ -626,9 +626,9 @@ def parse_maven_param(confs, chain=False, scratch=False, section=None):
         if section in builds:
             builds = {section: builds[section]}
         else:
-            raise ValueError, "Section %s does not exist in: %s" % (section, ', '.join(confs))
+            raise ValueError("Section %s does not exist in: %s" % (section, ', '.join(confs)))
     elif len(builds) > 1:
-        raise ValueError, "Multiple sections in: %s, you must specify the section" % ', '.join(confs)
+        raise ValueError("Multiple sections in: %s, you must specify the section" % ', '.join(confs))
     return builds
 
 def parse_maven_chain(confs, scratch=False):
@@ -646,5 +646,5 @@ def parse_maven_chain(confs, scratch=False):
     try:
         tsort(depmap)
     except ValueError:
-        raise ValueError, 'No possible build order, missing/circular dependencies'
+        raise ValueError('No possible build order, missing/circular dependencies')
     return builds
diff --git a/plugins/builder/runroot.py b/plugins/builder/runroot.py
index 0baa11d..57834da 100644
--- a/plugins/builder/runroot.py
+++ b/plugins/builder/runroot.py
@@ -120,10 +120,10 @@ class RunRootTask(tasks.BaseTaskHandler):
             #  c) is canonical
             host_arches = self.session.host.getHost()['arches']
             if not host_arches:
-                raise koji.BuildError, "No arch list for this host"
+                raise koji.BuildError("No arch list for this host")
             tag_arches = self.session.getBuildConfig(root)['arches']
             if not tag_arches:
-                raise koji.BuildError, "No arch list for tag: %s" % root
+                raise koji.BuildError("No arch list for tag: %s" % root)
             #index canonical host arches
             host_arches = dict([(koji.canonArch(a),1) for a in host_arches.split()])
             #pick the first suitable match from tag's archlist
@@ -134,15 +134,15 @@ class RunRootTask(tasks.BaseTaskHandler):
                     break
             else:
                 #no overlap
-                raise koji.BuildError, "host does not match tag arches: %s (%s)" % (root, tag_arches)
+                raise koji.BuildError("host does not match tag arches: %s (%s)" % (root, tag_arches))
         else:
             br_arch = arch
         if repo_id:
             repo_info = self.session.repoInfo(repo_id, strict=True)
             if repo_info['tag_name'] != root:
-                raise koji.BuildError, "build tag (%s) does not match repo tag (%s)" % (root, repo_info['tag_name'])
+                raise koji.BuildError("build tag (%s) does not match repo tag (%s)" % (root, repo_info['tag_name']))
             if repo_info['state'] not in (koji.REPO_STATES['READY'], koji.REPO_STATES['EXPIRED']):
-                raise koji.BuildError, "repos in the %s state may not be used by runroot" % koji.REPO_STATES[repo_info['state']]
+                raise koji.BuildError("repos in the %s state may not be used by runroot" % koji.REPO_STATES[repo_info['state']])
         else:
             repo_info = self.session.getRepo(root)
         if not repo_info:
@@ -169,7 +169,7 @@ class RunRootTask(tasks.BaseTaskHandler):
                 status = broot.mock(pkgcmd)
                 self.session.host.updateBuildRootList(broot.id, broot.getPackageList())
                 if not _isSuccess(status):
-                    raise koji.BuildrootError, _parseStatus(status, pkgcmd)
+                    raise koji.BuildrootError(_parseStatus(status, pkgcmd))
 
             if isinstance(command, str):
                 cmdstr = command
@@ -218,7 +218,7 @@ class RunRootTask(tasks.BaseTaskHandler):
         if _isSuccess(rv):
             return '%s completed successfully' % cmd
         else:
-            raise koji.BuildrootError, _parseStatus(rv, cmd)
+            raise koji.BuildrootError(_parseStatus(rv, cmd))
 
     def do_extra_mounts(self, rootdir, mounts):
         mnts = []
@@ -315,7 +315,7 @@ class RunRootTask(tasks.BaseTaskHandler):
             msg = "Unable to unmount: %s" % ', '.join(failed)
             self.logger.warn(msg)
             if fatal:
-                raise koji.GenericError, msg
+                raise koji.GenericError(msg)
         else:
             # remove the mount list when everything is unmounted
             try:
diff --git a/plugins/hub/messagebus.py b/plugins/hub/messagebus.py
index 272a387..10c8b00 100644
--- a/plugins/hub/messagebus.py
+++ b/plugins/hub/messagebus.py
@@ -103,7 +103,7 @@ def get_sender():
         keytab = krbV.Keytab(name='FILE:' + config.get('broker', 'keytab'), context=ctx)
         ccache.init_creds_keytab(principal=cprinc, keytab=keytab)
     else:
-        raise PluginError, 'unsupported auth type: %s' % auth
+        raise PluginError('unsupported auth type: %s' % auth)
 
     url += config.get('broker', 'host') + ':'
     url += config.get('broker', 'port')
@@ -223,7 +223,7 @@ def send_message(cbtype, *args, **kws):
         headers = get_message_headers(msgtype, *args, **kws)
         message = qpid.messaging.Message(properties=headers, content=data)
     else:
-        raise PluginError, 'unsupported exchange type: %s' % exchange_type
+        raise PluginError('unsupported exchange type: %s' % exchange_type)
 
     sender.send(message, sync=True, timeout=config.getfloat('broker', 'timeout'))
     sender.close(timeout=config.getfloat('broker', 'timeout'))
diff --git a/plugins/hub/rpm2maven.py b/plugins/hub/rpm2maven.py
index 484a319..b1310ea 100644
--- a/plugins/hub/rpm2maven.py
+++ b/plugins/hub/rpm2maven.py
@@ -62,9 +62,9 @@ def expand_rpm(filepath, tmpdir):
                             stdout=devnull, stderr=devnull,
                             close_fds=True)
     if rpm2cpio.wait() != 0 or cpio.wait() != 0:
-        raise koji.CallbackError, 'error extracting files from %s, ' \
+        raise koji.CallbackError('error extracting files from %s, ' \
               'rpm2cpio returned %s, cpio returned %s' % \
-              (filepath, rpm2cpio.wait(), cpio.wait())
+              (filepath, rpm2cpio.wait(), cpio.wait()))
     devnull.close()
 
 def scan_and_import(buildinfo, rpminfo, tmpdir):
diff --git a/plugins/hub/runroot_hub.py b/plugins/hub/runroot_hub.py
index 3238739..ab1ebbe 100644
--- a/plugins/hub/runroot_hub.py
+++ b/plugins/hub/runroot_hub.py
@@ -40,7 +40,7 @@ def runroot(tagInfo, arch, command, channel=None, **opts):
         #not all arches can generate a proper buildroot for all tags
         tag = kojihub.get_tag(tagInfo)
         if not tag['arches']:
-            raise koji.GenericError, 'no arches defined for tag %s' % tag['name']
+            raise koji.GenericError('no arches defined for tag %s' % tag['name'])
 
         #get all known arches for the system
         fullarches = kojihub.get_all_arches()
@@ -53,8 +53,8 @@ def runroot(tagInfo, arch, command, channel=None, **opts):
             chanarches = get_channel_arches(taskopts['channel'])
             choices = [x for x in tagarches if x in chanarches]
             if not choices:
-                raise koji.GenericError, 'no common arches for tag/channel: %s/%s' \
-                            % (tagInfo, taskopts['channel'])
+                raise koji.GenericError('no common arches for tag/channel: %s/%s' \
+                            % (tagInfo, taskopts['channel']))
             taskopts['arch'] = koji.canonArch(random.choice(choices))
 
     args = koji.encode_args(tagInfo, arch, command,**opts)
diff --git a/util/koji-gc b/util/koji-gc
index 23d14d3..d474ce4 100755
--- a/util/koji-gc
+++ b/util/koji-gc
@@ -307,7 +307,7 @@ def parse_duration(str):
                 if unit:
                     break
             else:
-                raise ValueError, "Invalid time interval: %s" % str
+                raise ValueError("Invalid time interval: %s" % str)
         if unit is None:
             x = x.lower()
             for names in time_unit_aliases:
@@ -318,7 +318,7 @@ def parse_duration(str):
                 if unit:
                     break
             else:
-                raise ValueError, "Invalid time interval: %s" % str
+                raise ValueError("Invalid time interval: %s" % str)
         ret += n * time_units[unit]
         n = None
         unit = None
@@ -720,7 +720,7 @@ class AgePruneTest(koji.policy.BaseSimpleTest):
         self.cmp, value = str.split(None, 2)[1:]
         self.func = self.cmp_idx.get(self.cmp, None)
         if self.func is None:
-            raise Exception, "Invalid comparison in test: %s" % str
+            raise Exception("Invalid comparison in test: %s" % str)
         self.span = parse_duration(value)
 
     def run(self, data):
@@ -784,7 +784,7 @@ def handle_prune():
     policies = scan_policies(options.config.get('prune', 'policy'))
     for action in policies.all_actions():
         if action not in ("keep", "untag", "skip"):
-            raise Exception, "Invalid action: %s" % action
+            raise Exception("Invalid action: %s" % action)
     if options.debug:
         pprint.pprint(policies.ruleset)
     #get tags
diff --git a/util/koji-shadow b/util/koji-shadow
index e028026..5ac6a25 100755
--- a/util/koji-shadow
+++ b/util/koji-shadow
@@ -266,7 +266,7 @@ def parse_duration(str):
                 if unit:
                     break
             else:
-                raise ValueError, "Invalid time interval: %s" % str
+                raise ValueError("Invalid time interval: %s" % str)
         if unit is None:
             x = x.lower()
             for names in time_unit_aliases:
@@ -277,7 +277,7 @@ def parse_duration(str):
                 if unit:
                     break
             else:
-                raise ValueError, "Invalid time interval: %s" % str
+                raise ValueError("Invalid time interval: %s" % str)
         ret += n * time_units[unit]
         n = None
         unit = None
@@ -631,7 +631,7 @@ class BuildTracker(object):
                     continue
                 data = line.split()
                 if len(data) != 2:
-                    raise Exception, "Bad substitution: %s" % line
+                    raise Exception("Bad substitution: %s" % line)
                 match, replace = data
                 self.substitutions[match] = replace
 
diff --git a/util/kojira b/util/kojira
index 6f4bbe3..5512b34 100755
--- a/util/kojira
+++ b/util/kojira
@@ -92,7 +92,7 @@ class ManagedRepo(object):
         if self.state == koji.REPO_EXPIRED:
             return
         elif self.state == koji.REPO_DELETED:
-            raise koji.GenericError, "Repo already deleted"
+            raise koji.GenericError("Repo already deleted")
         self.logger.info("Expiring repo %s.." % self.repo_id)
         self.session.repoExpire(self.repo_id)
         self.state = koji.REPO_EXPIRED
@@ -157,7 +157,7 @@ class ManagedRepo(object):
                 return False
         self.logger.debug("Attempting to delete repo %s.." % self.repo_id)
         if self.state != koji.REPO_EXPIRED:
-            raise koji.GenericError, "Repo not expired"
+            raise koji.GenericError("Repo not expired")
         if self.session.repoDelete(self.repo_id) > 0:
             #cannot delete, we are referenced by a buildroot
             self.logger.debug("Cannot delete repo %s, still referenced" % self.repo_id)
diff --git a/vm/kojikamid.py b/vm/kojikamid.py
index 030aac8..0499c74 100755
--- a/vm/kojikamid.py
+++ b/vm/kojikamid.py
@@ -135,20 +135,20 @@ class WindowsBuild(object):
                 else:
                     self.logger.info('file %s exists', entry)
         if errors:
-            raise BuildError, 'error validating build environment: %s' % \
-                  ', '.join(errors)
+            raise BuildError('error validating build environment: %s' % \
+                  ', '.join(errors))
 
     def updateClam(self):
         """update ClamAV virus definitions"""
         ret, output = run(['/bin/freshclam', '--quiet'])
         if ret:
-            raise BuildError, 'could not update ClamAV database: %s' % output
+            raise BuildError('could not update ClamAV database: %s' % output)
 
     def checkEnv(self):
         """make the environment is fit for building in"""
         for tool in ['/bin/freshclam', '/bin/clamscan', '/bin/patch']:
             if not os.path.isfile(tool):
-                raise BuildError, '%s is missing from the build environment' % tool
+                raise BuildError('%s is missing from the build environment' % tool)
 
     def zipDir(self, rootdir, filename):
         rootbase = os.path.basename(rootdir)
@@ -187,7 +187,7 @@ class WindowsBuild(object):
                    os.path.isfile(os.path.join(patchdir, patch)) and \
                    patch.endswith('.patch')]
         if not patches:
-            raise BuildError, 'no patches found at %s' % patchdir
+            raise BuildError('no patches found at %s' % patchdir)
         patches.sort()
         for patch in patches:
             cmd = ['/bin/patch', '--verbose', '-d', sourcedir, '-p1', '-i', os.path.join(patchdir, patch)]
@@ -197,9 +197,9 @@ class WindowsBuild(object):
         """Load build configuration from the spec file."""
         specfiles = [spec for spec in os.listdir(self.spec_dir) if spec.endswith('.ini')]
         if len(specfiles) == 0:
-            raise BuildError, 'No .ini file found'
+            raise BuildError('No .ini file found')
         elif len(specfiles) > 1:
-            raise BuildError, 'Multiple .ini files found'
+            raise BuildError('Multiple .ini files found')
 
         conf = ConfigParser()
         conf.read(os.path.join(self.spec_dir, specfiles[0]))
@@ -290,7 +290,7 @@ class WindowsBuild(object):
         """Create the buildroot object on the hub."""
         repo_id = self.task_opts.get('repo_id')
         if not repo_id:
-            raise BuildError, 'repo_id must be specified'
+            raise BuildError('repo_id must be specified')
         self.buildroot_id = self.server.initBuildroot(repo_id, self.platform)
 
     def expireBuildroot(self):
@@ -317,8 +317,8 @@ class WindowsBuild(object):
         digest = checksum.hexdigest()
         # rpms don't have a md5sum in the fileinfo, but check it for everything else
         if ('md5sum' in fileinfo) and (digest != fileinfo['md5sum']):
-            raise BuildError, 'md5 checksum validation failed for %s, %s (computed) != %s (provided)' % \
-                  (destpath, digest, fileinfo['md5sum'])
+            raise BuildError('md5 checksum validation failed for %s, %s (computed) != %s (provided)' % \
+                  (destpath, digest, fileinfo['md5sum']))
         self.logger.info('Retrieved %s (%s bytes, md5: %s)', destpath, offset, digest)
 
     def fetchBuildReqs(self):
@@ -408,7 +408,7 @@ class WindowsBuild(object):
         cmd = ['cmd.exe', '/C', 'C:\\Windows\\Temp\\' + os.path.basename(tmpname)]
         ret, output = run(cmd, chdir=self.source_dir)
         if ret:
-            raise BuildError, 'build command failed, see build.log for details'
+            raise BuildError('build command failed, see build.log for details')
 
     def bashBuild(self):
         """Do the build: run the execute line(s) with bash"""
@@ -440,7 +440,7 @@ class WindowsBuild(object):
         cmd = ['/bin/bash', '-e', '-x', tmpname]
         ret, output = run(cmd, chdir=self.source_dir)
         if ret:
-            raise BuildError, 'build command failed, see build.log for details'
+            raise BuildError('build command failed, see build.log for details')
 
     def checkBuild(self):
         """Verify that the build completed successfully."""
@@ -467,13 +467,13 @@ class WindowsBuild(object):
                     errors.append('file %s does not exist' % entry)
         self.virusCheck(self.workdir)
         if errors:
-            raise BuildError, 'error validating build output: %s' % \
-                  ', '.join(errors)
+            raise BuildError('error validating build output: %s' % \
+                  ', '.join(errors))
 
     def virusCheck(self, path):
         """ensure a path is virus free with ClamAV. path should be absolute"""
         if not path.startswith('/'):
-            raise BuildError, 'Invalid path to scan for viruses: ' + path
+            raise BuildError('Invalid path to scan for viruses: ' + path)
         run(['/bin/clamscan', '--quiet', '--recursive', path], fatal=True)
 
     def gatherResults(self):
@@ -525,7 +525,7 @@ def run(cmd, chdir=None, fatal=False, log=True):
             msg += ', see %s for details' % (os.path.basename(logfd.name))
         else:
             msg += ', output: %s' % output
-        raise BuildError, msg
+        raise BuildError(msg)
     return ret, output
 
 def find_net_info():
@@ -534,7 +534,7 @@ def find_net_info():
     """
     ret, output = run(['ipconfig', '/all'], log=False)
     if ret:
-        raise RuntimeError, 'error running ipconfig, output was: %s' % output
+        raise RuntimeError('error running ipconfig, output was: %s' % output)
     macaddr = None
     gateway = None
     for line in output.splitlines():
diff --git a/vm/kojivmd b/vm/kojivmd
index 50d48a3..69e3188 100755
--- a/vm/kojivmd
+++ b/vm/kojivmd
@@ -309,7 +309,7 @@ class WinBuildTask(MultiPlatformTask):
         task_info = self.session.getTaskInfo(self.id)
         target_info = self.session.getBuildTarget(target)
         if not target_info:
-            raise koji.BuildError, 'unknown build target: %s' % target
+            raise koji.BuildError('unknown build target: %s' % target)
         dest_tag = self.session.getTag(target_info['dest_tag'], strict=True)
         build_tag = self.session.getTag(target_info['build_tag'], strict=True)
         repo_id = opts.get('repo_id')
@@ -317,7 +317,7 @@ class WinBuildTask(MultiPlatformTask):
             repo_info = session.repoInfo(repo_id)
             event_id = repo_info['create_event']
             if not repo_info:
-                raise koji.BuildError, 'invalid repo ID: %s' % repo_id
+                raise koji.BuildError('invalid repo ID: %s' % repo_id)
             policy_data = {
                 'user_id' : task_info['owner'],
                 'source' : source_url,
@@ -352,11 +352,11 @@ class WinBuildTask(MultiPlatformTask):
             if not opts.get('skip_tag'):
                 # Make sure package is on the list for this tag
                 if pkg_cfg is None:
-                    raise koji.BuildError, "package %s not in list for tag %s" \
-                            % (build_info['name'], dest_tag['name'])
+                    raise koji.BuildError("package %s not in list for tag %s" \
+                            % (build_info['name'], dest_tag['name']))
                 elif pkg_cfg['blocked']:
-                    raise koji.BuildError, "package %s is blocked for tag %s" \
-                            % (build_info['name'], dest_tag['name'])
+                    raise koji.BuildError("package %s is blocked for tag %s" \
+                            % (build_info['name'], dest_tag['name']))
 
             build_info = self.session.host.initWinBuild(self.id, build_info,
                                                         koji.util.dslice(results, ['platform']))
@@ -424,8 +424,8 @@ class VMExecTask(BaseTaskHandler):
         output, dummy = proc.communicate()
         ret = proc.wait()
         if ret:
-            raise koji.BuildError, 'unable to create qcow2 image, "%s" returned %s; output was: %s' % \
-                  (' '.join(cmd), ret, output)
+            raise koji.BuildError('unable to create qcow2 image, "%s" returned %s; output was: %s' % \
+                  (' '.join(cmd), ret, output))
         vm_user = pwd.getpwnam(self.options.vmuser)
         os.chown(new_path, vm_user.pw_uid, vm_user.pw_gid)
         return new_path
@@ -520,7 +520,7 @@ class VMExecTask(BaseTaskHandler):
         ctx = doc.xpathNewContext()
         nodelist = ctx.xpathEval('/domain/devices/interface[@type="network"]/mac')
         if not nodelist:
-            raise koji.BuildError, 'no network interfaces configured for %s' % vm.name()
+            raise koji.BuildError('no network interfaces configured for %s' % vm.name())
         addr = nodelist[0].prop('address')
         ctx.xpathFreeContext()
         doc.freeDoc()
@@ -567,7 +567,7 @@ class VMExecTask(BaseTaskHandler):
         builds = self.session.getLatestBuilds(tag, package=package,
                                               event=repo_info['create_event'])
         if not builds:
-            raise koji.BuildError, 'no build of package %s in tag %s' % (package, tag)
+            raise koji.BuildError('no build of package %s in tag %s' % (package, tag))
         build = builds[0]
         maven_build = self.session.getMavenBuild(build['id'])
         if maven_build:
@@ -663,7 +663,7 @@ class VMExecTask(BaseTaskHandler):
                 remote_url = remote_pi.winbuild(buildinfo) + '/' + \
                              fileinfo['localpath']
             else:
-                raise koji.BuildError, 'unsupported file type: %s' % type
+                raise koji.BuildError('unsupported file type: %s' % type)
             koji.ensuredir(os.path.dirname(localpath))
             urlgrabber.urlgrab(remote_url, filename=localpath)
 
@@ -689,21 +689,21 @@ class VMExecTask(BaseTaskHandler):
     def upload(self, path, offset, contents):
         local_path = os.path.abspath(os.path.join(self.output_dir, path))
         if not local_path.startswith(self.output_dir):
-            raise koji.BuildError, 'invalid upload path: %s' % path
+            raise koji.BuildError('invalid upload path: %s' % path)
         koji.ensuredir(os.path.dirname(local_path))
         # accept offset as a str to avoid problems with files larger than 2**32
         offset = int(offset)
         if offset == 0:
             if os.path.exists(local_path):
-                raise koji.BuildError, 'cannot overwrite %s' % local_path
+                raise koji.BuildError('cannot overwrite %s' % local_path)
             fobj = file(local_path, 'w')
         else:
             if not os.path.isfile(local_path):
-                raise koji.BuildError, '% does not exist' % local_path
+                raise koji.BuildError('% does not exist' % local_path)
             size = os.path.getsize(local_path)
             if offset != size:
-                raise koji.BuildError, 'cannot write to %s at offset %s, size is %s' % \
-                      (local_path, offset, size)
+                raise koji.BuildError('cannot write to %s at offset %s, size is %s' % \
+                      (local_path, offset, size))
             fobj = file(local_path, 'r+')
             fobj.seek(offset)
         data = base64.b64decode(contents)
@@ -723,16 +723,16 @@ class VMExecTask(BaseTaskHandler):
     def verifyChecksum(self, path, checksum, algo='sha1'):
         local_path = os.path.abspath(os.path.join(self.output_dir, path))
         if not local_path.startswith(self.output_dir):
-            raise koji.BuildError, 'invalid path: %s' % path
+            raise koji.BuildError('invalid path: %s' % path)
         if not os.path.isfile(local_path):
-            raise koji.BuildError, '%s does not exist' % local_path
+            raise koji.BuildError('%s does not exist' % local_path)
 
         if algo == 'sha1':
             sum = koji.util.sha1_constructor()
         elif algo == 'md5':
             sum = koji.util.md5_constructor()
         else:
-            raise koji.BuildError, 'unsupported checksum algorithm: %s' % algo
+            raise koji.BuildError('unsupported checksum algorithm: %s' % algo)
 
         fobj = file(local_path, 'r')
         while True:
@@ -744,8 +744,8 @@ class VMExecTask(BaseTaskHandler):
         if sum.hexdigest() == checksum:
             return True
         else:
-            raise koji.BuildError, '%s checksum validation failed for %s, %s (computed) != %s (provided)' % \
-                  (algo, local_path, sum.hexdigest(), checksum)
+            raise koji.BuildError('%s checksum validation failed for %s, %s (computed) != %s (provided)' % \
+                  (algo, local_path, sum.hexdigest(), checksum))
 
     def closeTask(self, output):
         self.output = output
@@ -809,8 +809,8 @@ class VMExecTask(BaseTaskHandler):
             self.logger.info('Started VM %s', clone_name)
         except libvirt.libvirtError, e:
             self.logger.error('error starting VM %s', clone_name, exc_info=True)
-            raise koji.PreBuildError, 'error starting VM %s, error was: %s' % \
-                  (clone_name, e)
+            raise koji.PreBuildError('error starting VM %s, error was: %s' % \
+                  (clone_name, e))
 
         start = time.time()
         while True:
@@ -819,7 +819,7 @@ class VMExecTask(BaseTaskHandler):
             if info[0] in (libvirt.VIR_DOMAIN_CRASHED, libvirt.VIR_DOMAIN_SHUTOFF):
                 self.logger.warn('VM %s crashed', clone_name)
                 self.server.server_close()
-                raise koji.BuildError, 'VM %s crashed' % clone_name
+                raise koji.BuildError('VM %s crashed' % clone_name)
             if self.success is None:
                 # task is still running
                 # make sure it hasn't exceeded the timeout
@@ -827,8 +827,8 @@ class VMExecTask(BaseTaskHandler):
                 if mins > timeout:
                     vm.destroy()
                     self.server.server_close()
-                    raise koji.BuildError, 'Task did not complete after %.2f minutes, VM %s has been destroyed' % \
-                          (mins, clone_name)
+                    raise koji.BuildError('Task did not complete after %.2f minutes, VM %s has been destroyed' % \
+                          (mins, clone_name))
             else:
                 vm.destroy()
                 self.server.server_close()
@@ -836,7 +836,7 @@ class VMExecTask(BaseTaskHandler):
                 if self.success:
                     return self.output
                 else:
-                    raise koji.BuildError, self.output
+                    raise koji.BuildError(self.output)
 
 class VMTaskManager(TaskManager):
     def __init__(self, options, session):
@@ -858,7 +858,7 @@ class VMTaskManager(TaskManager):
             if port in ports:
                 return False
             if macaddr in self.macaddrs:
-                raise koji.PreBuildError, 'duplicate MAC address: %s' % macaddr
+                raise koji.PreBuildError('duplicate MAC address: %s' % macaddr)
             self.macaddrs[macaddr] = (vm_name, task_id, port)
             self.logger.info('registered MAC address %s for VM %s (task ID %s, port %s)', macaddr, vm_name, task_id, port)
             return True
@@ -876,7 +876,7 @@ class VMTaskManager(TaskManager):
             if data:
                 return data[2]
             else:
-                raise koji.PreBuildError, 'unknown MAC address: %s' % macaddr
+                raise koji.PreBuildError('unknown MAC address: %s' % macaddr)
         finally:
             self.macaddr_lock.release()
 
@@ -905,7 +905,7 @@ class VMTaskManager(TaskManager):
     def checkDisk(self):
         if not os.path.exists(self.options.imagedir):
             self.logger.error('No such directory: %s', self.options.imagedir)
-            raise IOError, 'No such directory: %s' % self.options.imagedir
+            raise IOError('No such directory: %s' % self.options.imagedir)
         fs_stat = os.statvfs(self.options.imagedir)
         available = fs_stat.f_bavail * fs_stat.f_bsize
         availableMB = available / 1024 / 1024
diff --git a/www/kojiweb/index.py b/www/kojiweb/index.py
index 7f4e4dd..e862493 100644
--- a/www/kojiweb/index.py
+++ b/www/kojiweb/index.py
@@ -49,7 +49,7 @@ def _setUserCookie(environ, user):
     # someone is not using an expired cookie
     value = user + ':' + str(int(time.time()))
     if not options['Secret'].value:
-        raise koji.AuthError, 'Unable to authenticate, server secret not configured'
+        raise koji.AuthError('Unable to authenticate, server secret not configured')
     shasum = sha1_constructor(value)
     shasum.update(options['Secret'].value)
     value = "%s:%s" % (shasum.hexdigest(), value)
@@ -86,7 +86,7 @@ def _getUserCookie(environ):
         return None
     sig, value = parts
     if not options['Secret'].value:
-        raise koji.AuthError, 'Unable to authenticate, server secret not configured'
+        raise koji.AuthError('Unable to authenticate, server secret not configured')
     shasum = sha1_constructor(value)
     shasum.update(options['Secret'].value)
     if shasum.hexdigest() != sig:
@@ -129,16 +129,16 @@ def _assertLogin(environ):
     session = environ['koji.session']
     options = environ['koji.options']
     if 'koji.currentLogin' not in environ or 'koji.currentUser' not in environ:
-        raise StandardError, '_getServer() must be called before _assertLogin()'
+        raise StandardError('_getServer() must be called before _assertLogin()')
     elif environ['koji.currentLogin'] and environ['koji.currentUser']:
         if options['WebCert']:
             if not _sslLogin(environ, session, environ['koji.currentLogin']):
-                raise koji.AuthError, 'could not login %s via SSL' % environ['koji.currentLogin']
+                raise koji.AuthError('could not login %s via SSL' % environ['koji.currentLogin'])
         elif options['WebPrincipal']:
             if not _krbLogin(environ, environ['koji.session'], environ['koji.currentLogin']):
-                raise koji.AuthError, 'could not login using principal: %s' % environ['koji.currentLogin']
+                raise koji.AuthError('could not login using principal: %s' % environ['koji.currentLogin'])
         else:
-            raise koji.AuthError, 'KojiWeb is incorrectly configured for authentication, contact the system administrator'
+            raise koji.AuthError('KojiWeb is incorrectly configured for authentication, contact the system administrator')
 
         # verify a valid authToken was passed in to avoid CSRF
         authToken = environ['koji.form'].getfirst('a', '')
@@ -166,7 +166,7 @@ def _getServer(environ):
     if environ['koji.currentLogin']:
         environ['koji.currentUser'] = session.getUser(environ['koji.currentLogin'])
         if not environ['koji.currentUser']:
-            raise koji.AuthError, 'could not get user for principal: %s' % environ['koji.currentLogin']
+            raise koji.AuthError('could not get user for principal: %s' % environ['koji.currentLogin'])
         _setUserCookie(environ, environ['koji.currentLogin'])
     else:
         environ['koji.currentUser'] = None
@@ -229,30 +229,30 @@ def login(environ, page=None):
             return
 
         if environ.get('SSL_CLIENT_VERIFY') != 'SUCCESS':
-            raise koji.AuthError, 'could not verify client: %s' % environ.get('SSL_CLIENT_VERIFY')
+            raise koji.AuthError('could not verify client: %s' % environ.get('SSL_CLIENT_VERIFY'))
 
         # use the subject's common name as their username
         username = environ.get('SSL_CLIENT_S_DN_CN')
         if not username:
-            raise koji.AuthError, 'unable to get user information from client certificate'
+            raise koji.AuthError('unable to get user information from client certificate')
 
         if not _sslLogin(environ, session, username):
-            raise koji.AuthError, 'could not login %s using SSL certificates' % username
+            raise koji.AuthError('could not login %s using SSL certificates' % username)
 
         authlogger.info('Successful SSL authentication by %s', username)
 
     elif options['WebPrincipal']:
         principal = environ.get('REMOTE_USER')
         if not principal:
-            raise koji.AuthError, 'configuration error: mod_auth_kerb should have performed authentication before presenting this page'
+            raise koji.AuthError('configuration error: mod_auth_kerb should have performed authentication before presenting this page')
 
         if not _krbLogin(environ, session, principal):
-            raise koji.AuthError, 'could not login using principal: %s' % principal
+            raise koji.AuthError('could not login using principal: %s' % principal)
 
         username = principal
         authlogger.info('Successful Kerberos authentication by %s', username)
     else:
-        raise koji.AuthError, 'KojiWeb is incorrectly configured for authentication, contact the system administrator'
+        raise koji.AuthError('KojiWeb is incorrectly configured for authentication, contact the system administrator')
 
     _setUserCookie(environ, username)
     # To protect the session cookie, we must forceSSL
@@ -321,7 +321,7 @@ def notificationedit(environ, notificationID):
     notificationID = int(notificationID)
     notification = server.getBuildNotification(notificationID)
     if notification == None:
-        raise koji.GenericError, 'no notification with ID: %i' % notificationID
+        raise koji.GenericError('no notification with ID: %i' % notificationID)
 
     form = environ['koji.form']
 
@@ -368,7 +368,7 @@ def notificationcreate(environ):
     if form.has_key('add'):
         user = environ['koji.currentUser']
         if not user:
-            raise koji.GenericError, 'not logged-in'
+            raise koji.GenericError('not logged-in')
 
         package_id = form.getfirst('package')
         if package_id == 'all':
@@ -410,7 +410,7 @@ def notificationdelete(environ, notificationID):
     notificationID = int(notificationID)
     notification = server.getBuildNotification(notificationID)
     if not notification:
-        raise koji.GenericError, 'no notification with ID: %i' % notificationID
+        raise koji.GenericError('no notification with ID: %i' % notificationID)
 
     server.deleteNotification(notification['id'])
 
@@ -558,7 +558,7 @@ def taskinfo(environ, taskID):
     taskID = int(taskID)
     task = server.getTaskInfo(taskID, request=True)
     if not task:
-        raise koji.GenericError, 'invalid task ID: %s' % taskID
+        raise koji.GenericError('invalid task ID: %s' % taskID)
 
     values['title'] = koji.taskLabel(task) + ' | Task Info'
 
@@ -728,7 +728,7 @@ def getfile(environ, taskID, name, offset=None, size=None):
     output = server.listTaskOutput(taskID, stat=True)
     file_info = output.get(name)
     if not file_info:
-        raise koji.GenericError, 'no file "%s" output by task %i' % (name, taskID)
+        raise koji.GenericError('no file "%s" output by task %i' % (name, taskID))
 
     mime_guess = mimetypes.guess_type(name, strict=False)[0]
     if mime_guess:
@@ -848,7 +848,7 @@ def packageinfo(environ, packageID, tagOrder='name', tagStart=None, buildOrder='
         packageID = int(packageID)
     package = server.getPackage(packageID)
     if package == None:
-        raise koji.GenericError, 'invalid package ID: %s' % packageID
+        raise koji.GenericError('invalid package ID: %s' % packageID)
 
     values['title'] = package['name'] + ' | Package Info'
 
@@ -961,7 +961,7 @@ def tagedit(environ, tagID):
     tagID = int(tagID)
     tag = server.getTag(tagID)
     if tag == None:
-        raise koji.GenericError, 'no tag with ID: %i' % tagID
+        raise koji.GenericError('no tag with ID: %i' % tagID)
 
     form = environ['koji.form']
 
@@ -1001,7 +1001,7 @@ def tagdelete(environ, tagID):
     tagID = int(tagID)
     tag = server.getTag(tagID)
     if tag == None:
-        raise koji.GenericError, 'no tag with ID: %i' % tagID
+        raise koji.GenericError('no tag with ID: %i' % tagID)
 
     server.deleteTag(tag['id'])
 
@@ -1052,7 +1052,7 @@ def tagparent(environ, tagID, parentID, action):
             elif len(inheritanceData) == 1:
                 values['inheritanceData'] = inheritanceData[0]
             else:
-                raise koji.GenericError, 'tag %i has tag %i listed as a parent more than once' % (tag['id'], parent['id'])
+                raise koji.GenericError('tag %i has tag %i listed as a parent more than once' % (tag['id'], parent['id']))
 
             return _genHTML(environ, 'tagparent.chtml')
     elif action == 'remove':
@@ -1062,11 +1062,11 @@ def tagparent(environ, tagID, parentID, action):
                 datum['delete link'] = True
                 break
         else:
-            raise koji.GenericError, 'tag %i is not a parent of tag %i' % (parent['id'], tag['id'])
+            raise koji.GenericError('tag %i is not a parent of tag %i' % (parent['id'], tag['id']))
 
         server.setInheritanceData(tag['id'], data)
     else:
-        raise koji.GenericError, 'unknown action: %s' % action
+        raise koji.GenericError('unknown action: %s' % action)
 
     _redirect(environ, 'taginfo?tagID=%i' % tag['id'])
 
@@ -1448,22 +1448,22 @@ def fileinfo(environ, filename, rpmID=None, archiveID=None):
         rpmID = int(rpmID)
         rpm = server.getRPM(rpmID)
         if not rpm:
-            raise koji.GenericError, 'invalid RPM ID: %i' % rpmID
+            raise koji.GenericError('invalid RPM ID: %i' % rpmID)
         file = server.getRPMFile(rpm['id'], filename)
         if not file:
-            raise koji.GenericError, 'no file %s in RPM %i' % (filename, rpmID)
+            raise koji.GenericError('no file %s in RPM %i' % (filename, rpmID))
         values['rpm'] = rpm
     elif archiveID:
         archiveID = int(archiveID)
         archive = server.getArchive(archiveID)
         if not archive:
-            raise koji.GenericError, 'invalid archive ID: %i' % archiveID
+            raise koji.GenericError('invalid archive ID: %i' % archiveID)
         file = server.getArchiveFile(archive['id'], filename)
         if not file:
-            raise koji.GenericError, 'no file %s in archive %i' % (filename, archiveID)
+            raise koji.GenericError('no file %s in archive %i' % (filename, archiveID))
         values['archive'] = archive
     else:
-        raise koji.GenericError, 'either rpmID or archiveID must be specified'
+        raise koji.GenericError('either rpmID or archiveID must be specified')
 
     values['title'] = file['name'] + ' | File Info'
 
@@ -1478,11 +1478,11 @@ def cancelbuild(environ, buildID):
     buildID = int(buildID)
     build = server.getBuild(buildID)
     if build == None:
-        raise koji.GenericError, 'unknown build ID: %i' % buildID
+        raise koji.GenericError('unknown build ID: %i' % buildID)
 
     result = server.cancelBuild(build['id'])
     if not result:
-        raise koji.GenericError, 'unable to cancel build'
+        raise koji.GenericError('unable to cancel build')
 
     _redirect(environ, 'buildinfo?buildID=%i' % build['id'])
 
@@ -1525,7 +1525,7 @@ def hostinfo(environ, hostID=None, userID=None):
             hostID = int(hostID)
         host = server.getHost(hostID)
         if host == None:
-            raise koji.GenericError, 'invalid host ID: %s' % hostID
+            raise koji.GenericError('invalid host ID: %s' % hostID)
     elif userID:
         userID = int(userID)
         hosts = server.listHosts(userID=userID)
@@ -1533,9 +1533,9 @@ def hostinfo(environ, hostID=None, userID=None):
         if hosts:
             host = hosts[0]
         if host == None:
-            raise koji.GenericError, 'invalid host ID: %s' % userID
+            raise koji.GenericError('invalid host ID: %s' % userID)
     else:
-        raise koji.GenericError, 'hostID or userID must be provided'
+        raise koji.GenericError('hostID or userID must be provided')
 
     values['title'] = host['name'] + ' | Host Info'
 
@@ -1563,7 +1563,7 @@ def hostedit(environ, hostID):
     hostID = int(hostID)
     host = server.getHost(hostID)
     if host == None:
-        raise koji.GenericError, 'no host with ID: %i' % hostID
+        raise koji.GenericError('no host with ID: %i' % hostID)
 
     form = environ['koji.form']
 
@@ -1632,7 +1632,7 @@ def channelinfo(environ, channelID):
     channelID = int(channelID)
     channel = server.getChannel(channelID)
     if channel == None:
-        raise koji.GenericError, 'invalid channel ID: %i' % channelID
+        raise koji.GenericError('invalid channel ID: %i' % channelID)
 
     values['title'] = channel['name'] + ' | Channel Info'
 
@@ -1657,7 +1657,7 @@ def buildrootinfo(environ, buildrootID, builtStart=None, builtOrder=None, compon
     buildroot = server.getBuildroot(buildrootID)
 
     if buildroot == None:
-        raise koji.GenericError, 'unknown buildroot ID: %i' % buildrootID
+        raise koji.GenericError('unknown buildroot ID: %i' % buildrootID)
 
     elif buildroot['br_type'] == koji.BR_TYPES['STANDARD']:
         template = 'buildrootinfo.chtml'
@@ -1687,7 +1687,7 @@ def rpmlist(environ, type, buildrootID=None, imageID=None, start=None, order='nv
         buildroot = server.getBuildroot(buildrootID)
         values['buildroot'] = buildroot
         if buildroot == None:
-            raise koji.GenericError, 'unknown buildroot ID: %i' % buildrootID
+            raise koji.GenericError('unknown buildroot ID: %i' % buildrootID)
 
         if type == 'component':
             kojiweb.util.paginateMethod(server, values, 'listRPMs',
@@ -1700,7 +1700,7 @@ def rpmlist(environ, type, buildrootID=None, imageID=None, start=None, order='nv
                                         start=start, dataName='rpms',
                                         prefix='rpm', order=order)
         else:
-            raise koji.GenericError, 'unrecognized type of rpmlist'
+            raise koji.GenericError('unrecognized type of rpmlist')
 
     elif imageID != None:
         imageID = int(imageID)
@@ -1712,11 +1712,11 @@ def rpmlist(environ, type, buildrootID=None, imageID=None, start=None, order='nv
                                         start=start, dataName='rpms',
                                         prefix='rpm', order=order)
         else:
-            raise koji.GenericError, 'unrecognized type of image rpmlist'
+            raise koji.GenericError('unrecognized type of image rpmlist')
 
     else:
         # It is an error if neither buildrootID and imageID are defined.
-        raise koji.GenericError, 'Both buildrootID and imageID are None'
+        raise koji.GenericError('Both buildrootID and imageID are None')
 
     values['type'] = type
     values['order'] = order
@@ -1730,7 +1730,7 @@ def archivelist(environ, buildrootID, type, start=None, order='filename'):
     buildrootID = int(buildrootID)
     buildroot = server.getBuildroot(buildrootID)
     if buildroot == None:
-        raise koji.GenericError, 'unknown buildroot ID: %i' % buildrootID
+        raise koji.GenericError('unknown buildroot ID: %i' % buildrootID)
 
     if type == 'component':
         kojiweb.util.paginateMethod(server, values, 'listArchives', kw={'componentBuildrootID': buildroot['id']},
@@ -1739,7 +1739,7 @@ def archivelist(environ, buildrootID, type, start=None, order='filename'):
         kojiweb.util.paginateMethod(server, values, 'listArchives', kw={'buildrootID': buildroot['id']},
                                     start=start, dataName='archives', prefix='archive', order=order)
     else:
-        raise koji.GenericError, 'invalid type: %s' % type
+        raise koji.GenericError('invalid type: %s' % type)
 
     values['buildroot'] = buildroot
     values['type'] = type
@@ -1775,7 +1775,7 @@ def buildtargetinfo(environ, targetID=None, name=None):
         target = server.getBuildTarget(name)
 
     if target == None:
-        raise koji.GenericError, 'invalid build target: %s' % (targetID or name)
+        raise koji.GenericError('invalid build target: %s' % (targetID or name))
 
     values['title'] = target['name'] + ' | Build Target Info'
 
@@ -1800,7 +1800,7 @@ def buildtargetedit(environ, targetID):
 
     target = server.getBuildTarget(targetID)
     if target == None:
-        raise koji.GenericError, 'invalid build target: %s' % targetID
+        raise koji.GenericError('invalid build target: %s' % targetID)
 
     form = environ['koji.form']
 
@@ -1809,12 +1809,12 @@ def buildtargetedit(environ, targetID):
         buildTagID = int(form.getfirst('buildTag'))
         buildTag = server.getTag(buildTagID)
         if buildTag == None:
-            raise koji.GenericError, 'invalid tag ID: %i' % buildTagID
+            raise koji.GenericError('invalid tag ID: %i' % buildTagID)
 
         destTagID = int(form.getfirst('destTag'))
         destTag = server.getTag(destTagID)
         if destTag == None:
-            raise koji.GenericError, 'invalid tag ID: %i' % destTagID
+            raise koji.GenericError('invalid tag ID: %i' % destTagID)
 
         server.editBuildTarget(target['id'], name, buildTag['id'], destTag['id'])
 
@@ -1849,7 +1849,7 @@ def buildtargetcreate(environ):
         target = server.getBuildTarget(name)
 
         if target == None:
-            raise koji.GenericError, 'error creating build target "%s"' % name
+            raise koji.GenericError('error creating build target "%s"' % name)
 
         _redirect(environ, 'buildtargetinfo?targetID=%i' % target['id'])
     elif form.has_key('cancel'):
@@ -1873,7 +1873,7 @@ def buildtargetdelete(environ, targetID):
 
     target = server.getBuildTarget(targetID)
     if target == None:
-        raise koji.GenericError, 'invalid build target: %i' % targetID
+        raise koji.GenericError('invalid build target: %i' % targetID)
 
     server.deleteBuildTarget(target['id'])
 
@@ -2246,7 +2246,7 @@ def search(environ, start=None, order=None):
 
         infoURL = _infoURLs.get(type)
         if not infoURL:
-            raise koji.GenericError, 'unknown search type: %s' % type
+            raise koji.GenericError('unknown search type: %s' % type)
         values['infoURL'] = infoURL
         order = order or _DEFAULT_SEARCH_ORDER.get(type, 'name')
         values['order'] = order
diff --git a/www/kojiweb/wsgi_publisher.py b/www/kojiweb/wsgi_publisher.py
index 6fd7f04..76f78d5 100644
--- a/www/kojiweb/wsgi_publisher.py
+++ b/www/kojiweb/wsgi_publisher.py
@@ -146,7 +146,7 @@ class Dispatcher(object):
             # presumably we are configured by modpy options
             config = None
         else:
-            raise koji.GenericError, "Configuration missing"
+            raise koji.GenericError("Configuration missing")
 
         opts = {}
         for name, dtype, default in self.cfgmap:
@@ -198,7 +198,7 @@ class Dispatcher(object):
                 level = part
                 default = level
             if level not in valid_levels:
-                raise koji.GenericError, "Invalid log level: %s" % level
+                raise koji.GenericError("Invalid log level: %s" % level)
             #all our loggers start with koji
             if name == '':
                 name = 'koji'
diff --git a/www/lib/kojiweb/util.py b/www/lib/kojiweb/util.py
index 03d0272..aa6d61a 100644
--- a/www/lib/kojiweb/util.py
+++ b/www/lib/kojiweb/util.py
@@ -299,7 +299,7 @@ def paginateMethod(server, values, methodName, args=None, kw=None,
     if not start or start < 0:
         start = 0
     if not dataName:
-        raise StandardError, 'dataName must be specified'
+        raise StandardError('dataName must be specified')
 
     kw['queryOpts'] = {'countOnly': True}
     totalRows = getattr(server, methodName)(*args, **kw)
@@ -329,7 +329,7 @@ def paginateResults(server, values, methodName, args=None, kw=None,
     if not start or start < 0:
         start = 0
     if not dataName:
-        raise StandardError, 'dataName must be specified'
+        raise StandardError('dataName must be specified')
 
     kw['filterOpts'] = {'order': order,
                         'offset': start,

